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
10 changes: 5 additions & 5 deletions Src/IronPython.Modules/nt.cs
Original file line number Diff line number Diff line change
Expand Up @@ -378,7 +378,7 @@ public static int dup2(CodeContext/*!*/ context, int fd, int fd2) {
}

if (!fileManager.ValidateFdRange(fd2)) {
throw PythonOps.OSError(9, "Bad file descriptor");
throw PythonOps.OSError(PythonErrorNumber.EBADF, "Bad file descriptor");
}

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

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

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

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

return streams.Write(buffer);
} catch (Exception e) {
Expand Down
8 changes: 4 additions & 4 deletions Src/IronPython/Modules/_fileio.cs
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,7 @@ public FileIO(CodeContext/*!*/ context, string name, string mode = "r", bool clo
}

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

private static Stream OpenFile(CodeContext/*!*/ context, PlatformAdaptationLayer pal, string name, FileMode fileMode, FileAccess fileAccess, FileShare fileShare) {
if (string.IsNullOrWhiteSpace(name)) throw PythonOps.OSError(2, "No such file or directory", filename: name);
if (string.IsNullOrWhiteSpace(name)) throw PythonOps.OSError(PythonFileManager.ENOENT, "No such file or directory", filename: name);
try {
return pal.OpenFileStream(name, fileMode, fileAccess, fileShare, 1); // Use a 1 byte buffer size to disable buffering (if the FileStream implementation supports it).
} catch (UnauthorizedAccessException) {
throw PythonOps.OSError(13, "Permission denied", name);
throw PythonOps.OSError(PythonFileManager.EACCES, "Permission denied", name);
} catch (FileNotFoundException) {
throw PythonOps.OSError(2, "No such file or directory", name);
throw PythonOps.OSError(PythonFileManager.ENOENT, "No such file or directory", name);
} catch (IOException e) {
AddFilename(context, name, e);
throw;
Expand Down
4 changes: 3 additions & 1 deletion Src/IronPython/Modules/_io.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,8 @@ public static partial class PythonIOModule {
private static readonly object _blockingIOErrorKey = new object();
private static readonly object _unsupportedOperationKey = new object();

// Values of the O_flags below has to be identical with flags defined in PythonNT

// Values of the O_flags below have to be identical with flags defined in PythonNT

#region Generated Common O_Flags

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

#endregion


[SpecialName]
public static void PerformModuleReload(PythonContext/*!*/ context, PythonDictionary/*!*/ dict) {
context.EnsureModuleException(
Expand Down
30 changes: 24 additions & 6 deletions Src/IronPython/Runtime/PythonFileManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,7 @@ public void CloseStreams(PythonFileManager? manager) {
/// </summary>
/// <remarks>
/// PythonFileManager emulates a file descriptor table. On Windows, .NET uses Win32 API which uses file handles
/// rather than file descriptors. The emulation is necesary to support Python API, which in some places uses file descriptors.
/// rather than file descriptors. The emulation is necessary to support Python API, which in some places uses file descriptors.
///
/// The manager maintains a mapping between open files (or system file-like objects) and a "descriptor", being a small non-negative integer.
/// Unlike in CPython, the descriptors are allocated lazily, meaning they are allocated only when they become exposed (requested)
Expand All @@ -198,7 +198,7 @@ public void CloseStreams(PythonFileManager? manager) {
/// In such situations, only one of the FileIO may be opened with flag `closefd` (CPython rule).
///
/// The second lever of sharing of open files is below the file descriptor level. A file descriptor can be duplicated using dup/dup2,
/// but the duplicated descriptor is still refering to the same open file in the filesystem. In such case, the manager maintains
/// but the duplicated descriptor is still referring to the same open file in the filesystem. In such case, the manager maintains
/// a separate StreamBox for the duplicated descriptor, but the StreamBoxes for both descriptors share the underlying Streams.
/// Both such descriptors have to be closed independently by the user code (either explicitly by os.close(fd) or through close()
/// on the FileIO objects), but the underlying shared streams are closed only when all such duplicated descriptors are closed.
Expand All @@ -211,21 +211,39 @@ internal class PythonFileManager {
/// </summary>
public const int LIMIT_OFILE = 0x100000; // hard limit on Linux


// Values of the Errno codes below have to be identical with values defined in PythonErrorNumber

#region Generated Common Errno Codes

// *** BEGIN GENERATED CODE ***
// generated by function: generate_common_errno_codes from: generate_os_codes.py

internal const int ENOENT = 2;
internal const int EBADF = 9;
internal const int EACCES = 13;
internal const int EMFILE = 24;

// *** END GENERATED CODE ***

#endregion


private readonly object _synchObject = new();
private readonly Dictionary<int, StreamBox> _table = new();
private const int _offset = 3; // number of lowest keys that are not automatically allocated
private int _current = _offset; // lowest potentially unused key in _objects at or above _offset
private readonly ConcurrentDictionary<Stream, int> _refs = new();

// This version of Add is used with genuine file descriptors (Unix).
// Exception: support dup2 on all frameworks/platfroms.
// Exception: support dup2 on all frameworks/platforms.
public int Add(int id, StreamBox streams) {
ContractUtils.RequiresNotNull(streams, nameof(streams));
ContractUtils.Requires(streams.Id < 0, nameof(streams));
ContractUtils.Requires(id >= 0, nameof(id));
lock (_synchObject) {
if (_table.ContainsKey(id)) {
throw PythonOps.OSError(9, "Bad file descriptor", id.ToString());
throw PythonOps.OSError(EBADF, "Bad file descriptor", id.ToString());
}
streams.Id = id;
_table.Add(id, streams);
Expand All @@ -243,7 +261,7 @@ public int Add(StreamBox streams) {
while (_table.ContainsKey(_current)) {
_current++;
if (_current >= LIMIT_OFILE)
throw PythonOps.OSError(24, "Too many open files");
throw PythonOps.OSError(EMFILE, "Too many open files");
}
streams.Id = _current;
_table.Add(_current, streams);
Expand Down Expand Up @@ -280,7 +298,7 @@ public StreamBox GetStreams(int id) {
if (TryGetStreams(id, out StreamBox? streams)) {
return streams;
}
throw PythonOps.OSError(9, "Bad file descriptor");
throw PythonOps.OSError(EBADF, "Bad file descriptor");
}

public int GetOrAssignId(StreamBox streams) {
Expand Down
13 changes: 13 additions & 0 deletions Src/Scripts/generate_os_codes.py
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,18 @@ def darwin_code_expr(codes, fmt):
def linux_code_expr(codes, fmt):
return fmt(codes[linux_idx])

common_errno_codes = ['ENOENT', 'EBADF', 'EACCES', 'EMFILE']

def generate_common_errno_codes(cw):
for name in common_errno_codes:
codes = errorvalues[name]

value = windows_code_expr(codes, str)
if (all(c.isdigit() for c in value)):
cw.write(f"internal const int {name} = {value};")
else:
cw.write(f"internal static int {name} => {value};")

def generate_errno_names(cw):
def is_windows_alias(name):
return "WSA" + name in errorvalues and name in errorvalues and errorvalues["WSA" + name][windows_idx] == errorvalues[name][windows_idx]
Expand Down Expand Up @@ -208,6 +220,7 @@ def generate_common_O_flags(cw):
def main():
return generate(
("Errno Codes", generate_errno_codes),
("Common Errno Codes", generate_common_errno_codes),
("Errno Names", generate_errno_names),
("O_Flags", generate_all_O_flags),
("Common O_Flags", generate_common_O_flags),
Expand Down
Loading