8000 _ctypes pt. 3 by arihant2math · Pull Request #5530 · RustPython/RustPython · GitHub
[go: up one dir, main page]

Skip to content

_ctypes pt. 3 #5530

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

Merged
merged 10 commits into from
Mar 3, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
8000
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
35 changes: 18 additions & 17 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

134 changes: 134 additions & 0 deletions extra_tests/snippets/builtins_ctypes.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,29 @@
import os as _os, sys as _sys
import types as _types

from _ctypes import RTLD_LOCAL, RTLD_GLOBAL
from _ctypes import sizeof
from _ctypes import _SimpleCData
from _ctypes import CFuncPtr as _CFuncPtr

from struct import calcsize as _calcsize


DEFAULT_MODE = RTLD_LOCAL
if _os.name == "posix" and _sys.platform == "darwin":
# On OS X 10.3, we use RTLD_GLOBAL as default mode
# because RTLD_LOCAL does not work at least on some
# libraries. OS X 10.3 is Darwin 7, so we check for
# that.

if int(_os.uname().release.split('.')[0]) < 8:
DEFAULT_MODE = RTLD_GLOBAL

from _ctypes import FUNCFLAG_CDECL as _FUNCFLAG_CDECL, \
FUNCFLAG_PYTHONAPI as _FUNCFLAG_PYTHONAPI, \
FUNCFLAG_USE_ERRNO as _FUNCFLAG_USE_ERRNO, \
FUNCFLAG_USE_LASTERROR as _FUNCFLAG_USE_LASTERROR

def create_string_buffer(init, size=None):
"""create_string_buffer(aBytes) -> character array
create_string_buffer(anInteger) -> character array
Expand Down Expand Up @@ -131,3 +151,117 @@ class c_bool(_SimpleCData):
# s = create_string_buffer(b'\000' * 32)
assert i.value == 42
assert abs(f.value - 3.14) < 1e-06

if _os.name == "nt":
from _ctypes import LoadLibrary as _dlopen
from _ctypes import FUNCFLAG_STDCALL as _FUNCFLAG_STDCALL
elif _os.name == "posix":
from _ctypes import dlopen as _dlopen

class CDLL(object):
"""An instance of this class represents a loaded dll/shared
library, exporting functions using the standard C calling
convention (named 'cdecl' on Windows).

The exported functions can be accessed as attributes, or by
indexing with the function name. Examples:

<obj>.qsort -> callable object
<obj>['qsort'] -> callable object

Calling the functions releases the Python GIL during the call and
reacquires it afterwards.
"""
_func_flags_ = _FUNCFLAG_CDECL
_func_restype_ = c_int
# default values for repr
_name = '<uninitialized>'
_handle = 0
_FuncPtr = None

def __init__(self, name, mode=DEFAULT_MODE, handle=None,
use_errno=False,
use_last_error=False,
winmode=None):
self._name = name
flags = self._func_flags_
if use_errno:
flags |= _FUNCFLAG_USE_ERRNO
if use_last_error:
flags |= _FUNCFLAG_USE_LASTERROR
if _sys.platform.startswith("aix"):
"""When the name contains ".a(" and ends with ")",
e.g., "libFOO.a(libFOO.so)" - this is taken to be an
archive(member) syntax for dlopen(), and the mode is adjusted.
Otherwise, name is presented to dlopen() as a file argument.
"""
if name and name.endswith(")") and ".a(" in name:
mode |= ( _os.RTLD_MEMBER | _os.RTLD_NOW )
if _os.name == "nt":
if winmode is not None:
mode = winmode
else:
import nt
mode = 4096
if '/' in name or '\\' in name:
self._name = nt._getfullpathname(self._name)
mode |= nt._LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR

class _FuncPtr(_CFuncPtr):
_flags_ = flags
_restype_ = self._func_restype_
self._FuncPtr = _FuncPtr

if handle is None:
self._handle = _dlopen(self._name, mode)
else:
self._handle = handle

def __repr__(self):
return "<%s '%s', handle %x at %#x>" % \
(self.__class__.__name__, self._name,
(self._handle & (_sys.maxsize*2 + 1)),
id(self) & (_sys.maxsize*2 + 1))

def __getattr__(self, name):
if name.startswith('__') and name.endswith('__'):
raise AttributeError(name)
func = self.__getitem__(name)
setattr(self, name, func)
return func

def __getitem__(self, name_or_ordinal):
func = self._FuncPtr((name_or_ordinal, self))
if not isinstance(name_or_ordinal, int):
func.__name__ = name_or_ordinal
return func

class LibraryLoader(object):
def __init__(self, dlltype):
self._dlltype = dlltype

def __getattr__(self, name):
if name[0] == '_':
raise AttributeError(name)
try:
dll = self._dlltype(name)
except OSError:
raise AttributeError(name)
setattr(self, name, dll)
return dll

def __getitem__(self, name):
return getattr(self, name)

def LoadLibrary(self, name):
return self._dlltype(name)

__class_getitem__ = classmethod(_types.GenericAlias)

cdll = LibraryLoader(CDLL)

if _os.name == "posix" or _sys.platform == "darwin":
pass
else:
libc = cdll.msvcrt
print("rand", libc.rand())
5 changes: 4 additions & 1 deletion vm/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -100,9 +100,12 @@ uname = "0.1.1"
rustyline = { workspace = true }
which = "6"
errno = "0.3"
libloading = "0.8"
widestring = { workspace = true }

[target.'cfg(all(any(target_os = "linux", target_os = "macos", target_os = "windows"), not(any(target_env = "musl", target_env = "sgx"))))'.dependencies]
libffi = "3.2"
libloading = "0.8"

[target.'cfg(any(not(target_arch = "wasm32"), target_os = "wasi"))'.dependencies]
num_cpus = "1.13.1"

Expand Down
27 changes: 23 additions & 4 deletions vm/src/stdlib/ctypes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ pub(crate) mod _ctypes {
use super::base::PyCSimple;
use crate::builtins::PyTypeRef;
use crate::class::StaticType;
use crate::function::Either;
use crate::function::{Either, OptionalArg};
use crate::stdlib::ctypes::library;
use crate::{AsObject, PyObjectRef, PyResult, TryFromObject, VirtualMachine};
use crossbeam_utils::atomic::AtomicCell;
Expand Down Expand Up @@ -180,12 +180,31 @@ pub(crate) mod _ctypes {
}

#[pyfunction(name = "LoadLibrary")]
fn load_library(name: String, vm: &VirtualMachine) -> PyResult<usize> {
fn load_library_windows(
name: String,
_load_flags: OptionalArg<i32>,
vm: &VirtualMachine,
) -> PyResult<usize> {
// TODO: audit functions first
// TODO: load_flags
let cache = library::libcache();
let mut cache_write = cache.write();
let (id, _) = cache_write.get_or_insert_lib(&name, vm).unwrap();
Ok(id)
}

#[pyfunction(name = "dlopen")]
fn load_library_unix(
name: String,
_load_flags: OptionalArg<i32>,
vm: &VirtualMachine,
) -> PyResult<usize> {
// TODO: audit functions first
// TODO: load_flags
let cache = library::libcache();
let mut cache_write = cache.write();
let lib_ref = cache_write.get_or_insert_lib(&name, vm).unwrap();
Ok(lib_ref.get_pointer())
let (id, _) = cache_write.get_or_insert_lib(&name, vm).unwrap();
Ok(id)
}

#[pyfunction(name = "FreeLibrary")]
Expand Down
25 changes: 25 additions & 0 deletions vm/src/stdlib/ctypes/base.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,31 @@ use num_traits::ToPrimitive;
use rustpython_common::lock::PyRwLock;
use std::fmt::Debug;

pub fn ffi_type_from_str(_type_: &str) -> Option<libffi::middle::Type> {
match _type_ {
"c" => Some(libffi::middle::Type::u8()),
"u" => Some(libffi::middle::Type::u32()),
"b" => Some(libffi::middle::Type::i8()),
"B" => Some(libffi::middle::Type::u8()),
"h" => Some(libffi::middle::Type::i16()),
"H" => Some(libffi::middle::Type::u16()),
"i" => Some(libffi::middle::Type::i32()),
"I" => Some(libffi::middle::Type::u32()),
"l" => Some(libffi::middle::Type::i32()),
"L" => Some(libffi::middle::Type::u32()),
"q" => Some(libffi::middle::Type::i64()),
"Q" => Some(libffi::middle::Type::u64()),
"f" => Some(libffi::middle::Type::f32()),
"d" => Some(libffi::middle::Type::f64()),
"g" => Some(libffi::middle::Type::f64()),
"?" => Some(libffi::middle::Type::u8()),
"z" => Some(libffi::middle::Type::u64()),
"Z" => Some(libffi::middle::Type::u64()),
"P" => Some(libffi::middle::Type::u64()),
_ => None,
}
}

#[allow(dead_code)]
fn set_primitive(_type_: &str, value: &PyObjectRef, vm: &VirtualMachine) -> PyResult {
match _type_ {
Expand Down
Loading
Loading
0