Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement fcntl constants #1873

Merged
merged 6 commits into from
Jan 15, 2025
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
226 changes: 222 additions & 4 deletions Src/IronPython.Modules/fcntl.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,225 @@
using IronPython.Runtime;
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the Apache 2.0 License.
// See the LICENSE file in the project root for more information.

#nullable enable

using System;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;

using Mono.Unix.Native;

using IronPython.Runtime;

[assembly: PythonModule("fcntl", typeof(IronPython.Modules.PythonFcntl), PlatformsAttribute.PlatformFamily.Unix)]
namespace IronPython.Modules {
public static class PythonFcntl {
}
namespace IronPython.Modules;

[SupportedOSPlatform("linux")]
[SupportedOSPlatform("macos")]
public static class PythonFcntl {

public const string __doc__ = """
This module performs file control and I/O control on file descriptors.
It is an interface to the fcntl() and ioctl() Unix routines.
File descriptors can be obtained with the fileno() method of
a file object.
""";


// FD Flags
public static int FD_CLOEXEC = 1;
public static int FASYNC => RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? 0x0040 : 0x2000;


#region Generated FD Commands

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


[PythonHidden(PlatformID.MacOSX)]
[SupportedOSPlatform("linux")]
public static int F_ADD_SEALS => 1033;

public static int F_DUPFD => 0;

[PythonHidden(PlatformID.MacOSX)]
[SupportedOSPlatform("linux")]
public static int F_EXLCK => 4;

public static int F_GETFD => 1;

public static int F_GETFL => 3;

[PythonHidden(PlatformID.MacOSX)]
[SupportedOSPlatform("linux")]
public static int F_GETLEASE => 1025;

public static int F_GETLK => RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? 7 : 5;

[PythonHidden(PlatformID.MacOSX)]
[SupportedOSPlatform("linux")]
public static int F_GETLK64 => 5;

public static int F_GETOWN => RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? 5 : 9;

[PythonHidden(PlatformID.MacOSX)]
[SupportedOSPlatform("linux")]
public static int F_GETSIG => 11;

[PythonHidden(PlatformID.MacOSX)]
[SupportedOSPlatform("linux")]
public static int F_GET_SEALS => 1034;

[PythonHidden(PlatformID.Unix)]
[SupportedOSPlatform("macos")]
public static int F_NOCACHE => 48;

[PythonHidden(PlatformID.MacOSX)]
[SupportedOSPlatform("linux")]
public static int F_NOTIFY => 1026;

[PythonHidden(PlatformID.MacOSX)]
[SupportedOSPlatform("linux")]
public static int F_OFD_GETLK => 36;

[PythonHidden(PlatformID.MacOSX)]
[SupportedOSPlatform("linux")]
public static int F_OFD_SETLK => 37;

[PythonHidden(PlatformID.MacOSX)]
[SupportedOSPlatform("linux")]
public static int F_OFD_SETLKW => 38;

public static int F_RDLCK => RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? 1 : 0;

[PythonHidden(PlatformID.MacOSX)]
[SupportedOSPlatform("linux")]
public static int F_SEAL_GROW => 4;

[PythonHidden(PlatformID.MacOSX)]
[SupportedOSPlatform("linux")]
public static int F_SEAL_SEAL => 1;

[PythonHidden(PlatformID.MacOSX)]
[SupportedOSPlatform("linux")]
public static int F_SEAL_SHRINK => 2;

[PythonHidden(PlatformID.MacOSX)]
[SupportedOSPlatform("linux")]
public static int F_SEAL_WRITE => 8;

public static int F_SETFD => 2;

public static int F_SETFL => 4;

[PythonHidden(PlatformID.MacOSX)]
[SupportedOSPlatform("linux")]
public static int F_SETLEASE => 1024;

public static int F_SETLK => RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? 8 : 6;

[PythonHidden(PlatformID.MacOSX)]
[SupportedOSPlatform("linux")]
public static int F_SETLK64 => 6;

public static int F_SETLKW => RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? 9 : 7;

[PythonHidden(PlatformID.MacOSX)]
[SupportedOSPlatform("linux")]
public static int F_SETLKW64 => 7;

public static int F_SETOWN => RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? 6 : 8;

[PythonHidden(PlatformID.MacOSX)]
[SupportedOSPlatform("linux")]
public static int F_SETSIG => 10;

[PythonHidden(PlatformID.MacOSX)]
[SupportedOSPlatform("linux")]
public static int F_SHLCK => 8;

public static int F_UNLCK => 2;

public static int F_WRLCK => RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? 3 : 1;

// *** END GENERATED CODE ***

#endregion


#region Generated LOCK Flags

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


public static int LOCK_EX => 0x2;

[PythonHidden(PlatformID.MacOSX)]
[SupportedOSPlatform("linux")]
public static int LOCK_MAND => 0x20;

public static int LOCK_NB => 0x4;

[PythonHidden(PlatformID.MacOSX)]
[SupportedOSPlatform("linux")]
public static int LOCK_READ => 0x40;

[PythonHidden(PlatformID.MacOSX)]
[SupportedOSPlatform("linux")]
public static int LOCK_RW => 0xc0;

public static int LOCK_SH => 0x1;

public static int LOCK_UN => 0x8;

[PythonHidden(PlatformID.MacOSX)]
[SupportedOSPlatform("linux")]
public static int LOCK_WRITE => 0x80;

// *** END GENERATED CODE ***

#endregion


// Linux only
#region Generated Directory Notify Flags

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


[PythonHidden(PlatformID.MacOSX)]
[SupportedOSPlatform("linux")]
public static int DN_ACCESS => 0x1;

[PythonHidden(PlatformID.MacOSX)]
[SupportedOSPlatform("linux")]
public static int DN_ATTRIB => 0x20;

[PythonHidden(PlatformID.MacOSX)]
[SupportedOSPlatform("linux")]
public static int DN_CREATE => 0x4;

[PythonHidden(PlatformID.MacOSX)]
[SupportedOSPlatform("linux")]
public static int DN_DELETE => 0x8;

[PythonHidden(PlatformID.MacOSX)]
[SupportedOSPlatform("linux")]
public static int DN_MODIFY => 0x2;

[PythonHidden(PlatformID.MacOSX)]
[SupportedOSPlatform("linux")]
public static long DN_MULTISHOT => 0x80000000;

[PythonHidden(PlatformID.MacOSX)]
[SupportedOSPlatform("linux")]
public static int DN_RENAME => 0x10;

// *** END GENERATED CODE ***

#endregion
}
3 changes: 1 addition & 2 deletions Src/IronPython.Modules/posix.cs
Original file line number Diff line number Diff line change
Expand Up @@ -99,8 +99,7 @@ internal static int dupUnix(int fd, bool closeOnExec) {
int flags = Syscall.fcntl(fd2, FcntlCommand.F_GETFD);
if (flags == -1) throw GetLastUnixError();

const int FD_CLOEXEC = 1; // TODO: Move to module fcntl
flags |= FD_CLOEXEC;
flags |= PythonFcntl.FD_CLOEXEC;
flags = Syscall.fcntl(fd2, FcntlCommand.F_SETFD, flags);
if (flags == -1) throw GetLastUnixError();
} catch {
Expand Down
92 changes: 82 additions & 10 deletions Src/Scripts/generate_os_codes.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
# The .NET Foundation licenses this file to you under the Apache 2.0 License.
# See the LICENSE file in the project root for more information.

import re
from generate import generate
from collections import OrderedDict

Expand All @@ -16,11 +17,13 @@
darwin_aliases = {'EWOULDBLOCK': 'EAGAIN'}
aliases = {**linux_aliases, **darwin_aliases}


def set_value(codeval, name, value, index):
if name not in codeval:
codeval[name] = [None] * len(systems)
codeval[name][index] = value


def collect_errornames():
errorval = {}
for code in errorcode_linux:
Expand Down Expand Up @@ -102,6 +105,7 @@ def darwin_code_expr(codes, fmt):
def linux_code_expr(codes, fmt):
return fmt(codes[linux_idx])


common_errno_codes = ['ENOENT', 'E2BIG', 'ENOEXEC', 'EBADF', 'ECHILD', 'EAGAIN', 'ENOMEM', 'EACCES', 'EEXIST', 'EXDEV', 'ENOTDIR', 'EMFILE', 'ENOSPC', 'EPIPE', 'ENOTEMPTY', 'EILSEQ', 'EINVAL']

def generate_common_errno_codes(cw):
Expand All @@ -114,6 +118,7 @@ def generate_common_errno_codes(cw):
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 @@ -178,13 +183,17 @@ def collect_codes():

O_flagvalues = collect_codes()

def generate_O_flags(cw, flagvalues, access):
for name in flagvalues.keys():
codes = flagvalues[name]

def generate_codes(cw, codeval, access, fmt, unix_only=False):
for name in codeval.keys():
codes = codeval[name]
all_systems = set(systems)
if unix_only:
all_systems.discard(systems[windows_idx])
hidden_on = []
supported_on = set(systems)
supported_on = set(all_systems)
cw.writeline()
if codes[windows_idx] is None:
if codes[windows_idx] is None and not unix_only:
hidden_on = ["PlatformsAttribute.PlatformFamily.Windows"]
supported_on.discard(systems[windows_idx])
if codes[linux_idx] is None and codes[darwin_idx] is None:
Expand All @@ -201,20 +210,80 @@ def generate_O_flags(cw, flagvalues, access):
supported_on.discard(systems[darwin_idx])
if hidden_on and (access == 'public' or access == 'protected' or access == 'protected internal'):
cw.write(f"[PythonHidden({', '.join(hidden_on)})]")
if len(supported_on) != len(systems):
if len(supported_on) != len(all_systems):
for s in sorted(supported_on):
cw.write(f'[SupportedOSPlatform("{s}")]')

value = windows_code_expr(codes, fmt=hex)
cw.write(f"{access} static int {name} => {value};")
value = windows_code_expr(codes, fmt)
typ = "int"
if (all(c.isdigit() for c in value) or re.match(r'^0x[0-9a-fA-F]+$', value)):
n = eval(value)
if n > 2**31 - 1 or n < -2**31:
typ = "long"
cw.write(f"{access} static {typ} {name} => {value};")


def generate_all_O_flags(cw):
generate_O_flags(cw, O_flagvalues, 'public')
generate_codes(cw, O_flagvalues, 'public', hex)


common_O_flags = ['O_RDONLY', 'O_WRONLY', 'O_RDWR', 'O_APPEND', 'O_CREAT', 'O_TRUNC', 'O_EXCL', 'O_CLOEXEC', 'O_BINARY', 'O_NOINHERIT']

def generate_common_O_flags(cw):
generate_O_flags(cw, OrderedDict((f, O_flagvalues[f]) for f in common_O_flags), 'private')
generate_codes(cw, OrderedDict((f, O_flagvalues[f]) for f in common_O_flags), 'private', hex)


# python3 -c 'import fcntl;print(dict(sorted((s, getattr(fcntl, s)) for s in dir(fcntl) if s.startswith("F_"))))'
# Python 3.6.15 [GCC 12.2.0] on linux 6.10.14
FD_commands_linux = {'F_ADD_SEALS': 1033, 'F_DUPFD': 0, 'F_DUPFD_CLOEXEC': 1030, 'F_EXLCK': 4, 'F_GETFD': 1, 'F_GETFL': 3, 'F_GETLEASE': 1025, 'F_GETLK': 5, 'F_GETLK64': 5, 'F_GETOWN': 9, 'F_GETPIPE_SZ': 1032, 'F_GETSIG': 11, 'F_GET_SEALS': 1034, 'F_NOTIFY': 1026, 'F_OFD_GETLK': 36, 'F_OFD_SETLK': 37, 'F_OFD_SETLKW': 38, 'F_RDLCK': 0, 'F_SEAL_GROW': 4, 'F_SEAL_SEAL': 1, 'F_SEAL_SHRINK': 2, 'F_SEAL_WRITE': 8, 'F_SETFD': 2, 'F_SETFL': 4, 'F_SETLEASE': 1024, 'F_SETLK': 6, 'F_SETLK64': 6, 'F_SETLKW': 7, 'F_SETLKW64': 7, 'F_SETOWN': 8, 'F_SETPIPE_SZ': 1031, 'F_SETSIG': 10, 'F_SHLCK': 8, 'F_UNLCK': 2, 'F_WRLCK': 1}
# Unsupported by Mono.Unix 7.1.0-final.1.21458.1 on linux
FD_commands_linux_unsupported = ['F_DUPFD_CLOEXEC', 'F_GETPIPE_SZ', 'F_SETPIPE_SZ']
# Python 3.7.0 [Clang 4.0.1 ] on darwin 24.2.0
FD_commands_darwin = {'F_DUPFD': 0, 'F_DUPFD_CLOEXEC': 67, 'F_FULLFSYNC': 51, 'F_GETFD': 1, 'F_GETFL': 3, 'F_GETLK': 7, 'F_GETOWN': 5, 'F_NOCACHE': 48, 'F_RDLCK': 1, 'F_SETFD': 2, 'F_SETFL': 4, 'F_SETLK': 8, 'F_SETLKW': 9, 'F_SETOWN': 6, 'F_UNLCK': 2, 'F_WRLCK': 3}
# Unsupported by Mono.Unix 7.1.0-final.1.21458.1 on darwin
FD_commands_darwin_unsupported = ['F_DUPFD_CLOEXEC', 'F_FULLFSYNC']

def generate_FD_commands(cw):
codeval = {}
for name in FD_commands_linux:
if name not in FD_commands_linux_unsupported:
set_value(codeval, name, FD_commands_linux[name], linux_idx)
for name in FD_commands_darwin:
if name not in FD_commands_darwin_unsupported:
set_value(codeval, name, FD_commands_darwin[name], darwin_idx)
codeval = OrderedDict(sorted(codeval.items()))
generate_codes(cw, codeval, 'public', str, unix_only=True)


# python3 -c 'import fcntl;print(dict(sorted((s, getattr(fcntl, s)) for s in dir(fcntl) if s.startswith("DN_"))))'
# Python 3.6.15 [GCC 12.2.0] on linux 6.10.14
# Python 3.12.3 [GCC 13.2.0] on linux 6.8.0
DN_flags_linux = {'DN_ACCESS': 1, 'DN_ATTRIB': 32, 'DN_CREATE': 4, 'DN_DELETE': 8, 'DN_MODIFY': 2, 'DN_MULTISHOT': 2147483648, 'DN_RENAME': 16}

def generate_DN_flags(cw):
codeval = {}
for name in DN_flags_linux:
set_value(codeval, name, DN_flags_linux[name], linux_idx)
codeval = OrderedDict(sorted(codeval.items()))
generate_codes(cw, codeval, 'public', hex, unix_only=True)


# python3 -c 'import fcntl;print(dict(sorted((s, getattr(fcntl, s)) for s in dir(fcntl) if s.startswith("LOCK_"))))'
# Python 3.6.15 [GCC 12.2.0] on linux 6.10.14
# Python 3.12.3 [GCC 13.2.0] on linux 6.8.0
LOCK_flags_linux = {'LOCK_EX': 2, 'LOCK_MAND': 32, 'LOCK_NB': 4, 'LOCK_READ': 64, 'LOCK_RW': 192, 'LOCK_SH': 1, 'LOCK_UN': 8, 'LOCK_WRITE': 128}
# Python 3.7.0 [Clang 4.0.1 ] on darwin 24.2.0
# Python 3.12.0 [Clang 14.0.6 ] on darwin 24.2.0
LOCK_flags_darwin = {'LOCK_EX': 2, 'LOCK_NB': 4, 'LOCK_SH': 1, 'LOCK_UN': 8}

def generate_LOCK_flags(cw):
codeval = {}
for name in LOCK_flags_linux:
set_value(codeval, name, LOCK_flags_linux[name], linux_idx)
for name in LOCK_flags_darwin:
set_value(codeval, name, LOCK_flags_darwin[name], darwin_idx)
codeval = OrderedDict(sorted(codeval.items()))
generate_codes(cw, codeval, 'public', hex, unix_only=True)


def main():
Expand All @@ -224,6 +293,9 @@ def main():
("Errno Names", generate_errno_names),
("O_Flags", generate_all_O_flags),
("Common O_Flags", generate_common_O_flags),
("FD Commands", generate_FD_commands),
("Directory Notify Flags", generate_DN_flags),
("LOCK Flags", generate_LOCK_flags),
)

if __name__ == "__main__":
Expand Down