Skip to content

Commit 1b94d4d

Browse files
Refactor DLL loading logic and update paths for pyCTools integration
Moved `bin` inside pyCTools so it is easier for pip to install
1 parent f58dd10 commit 1b94d4d

File tree

4 files changed

+49
-82
lines changed

4 files changed

+49
-82
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,3 +115,4 @@ dmypy.json
115115
.pytype/
116116
cython_debug/
117117
/bin/
118+
/pyCTools/bin/

pyCTools/_loadDLL.py

Lines changed: 45 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -1,102 +1,68 @@
1+
import ctypes
12
import os
23
import platform
3-
import ctypes
4-
from typing import Callable, Optional, List
4+
from typing import Callable, Optional
55

66

77
def load_dll(
88
dll_prefix_name: str,
99
dll_load_func: Callable = ctypes.WinDLL,
10-
possible_bin_paths: Optional[List[str]] = None,
1110
hardcoded_dll_location: Optional[str] = None
1211
):
1312
"""
14-
Load a DLL file based on architecture and search paths, returning the loaded DLL handle.
15-
16-
This function attempts to locate and load a DLL file matching the current system's
17-
architecture (x86 or x64). The DLL filename is constructed by appending the architecture
18-
suffix to a given prefix name (e.g., "processInspect_x64.dll").
19-
20-
You can specify a hardcoded absolute DLL path or provide a list of possible relative
21-
distribution paths to search through. If both are provided, the function raises a ValueError.
22-
23-
Args:
24-
dll_prefix_name (str):
25-
The base name of the DLL without architecture or extension.
26-
For example, for "processInspect_x64.dll", the prefix is "processInspect".
27-
28-
dll_load_func (Callable, optional):
29-
The function used to load the DLL, typically `ctypes.WinDLL` or `ctypes.CDLL`.
30-
Defaults to `ctypes.WinDLL`.
31-
32-
possible_bin_paths (Optional[List[str]], optional):
33-
A list of possible relative paths where the DLL might reside. These are
34-
joined with the DLL filename and searched in order.
35-
Defaults to None, which triggers searching in default distribution directories.
36-
37-
hardcoded_dll_location (Optional[str], optional):
38-
An absolute path to the DLL. If provided, this path is used exclusively,
39-
bypassing the search logic.
40-
41-
Raises:
42-
ValueError:
43-
If both `hardcoded_dll_location` and `possible_dist_paths` are provided
44-
simultaneously.
45-
46-
FileNotFoundError:
47-
If the DLL cannot be found at the specified or searched locations.
48-
49-
Returns:
50-
ctypes.WinDLL or ctypes.CDLL:
51-
The loaded DLL object returned by the provided `dll_load_func`.
52-
53-
Example:
54-
dll = load_dll("processInspect")
13+
Loads a Windows DLL with architecture awareness and optional custom path.
14+
15+
This function attempts to load a dynamic-link library (DLL) based on the provided prefix name,
16+
automatically selecting the correct architecture (x64 or x86) according to the current Python
17+
interpreter. The DLL is expected to be located in a subdirectory structure of the form:
18+
./bin/{arch}/{dll_prefix_name}_{arch}.dll, where {arch} is either 'x64' or 'x86'.
19+
20+
Alternatively, a hardcoded DLL path can be provided, in which case the function will verify
21+
the existence of the file at that location before attempting to load it.
22+
23+
Parameters
24+
----------
25+
dll_prefix_name : str
26+
The prefix of the DLL filename (e.g., 'myLibrary' for 'myLibrary_x64.dll').
27+
dll_load_func : Callable, optional
28+
The function used to load the DLL. Defaults to ctypes.WinDLL, but can be replaced with
29+
ctypes.CDLL or any compatible loader for testing or non-standard DLLs.
30+
hardcoded_dll_location : Optional[str], optional
31+
An explicit path to the DLL file. If provided and valid, this path is used instead of
32+
constructing the path from the prefix and architecture.
33+
34+
Returns
35+
-------
36+
ctypes.CDLL or ctypes.WinDLL
37+
The loaded DLL object, as returned by the specified loader function.
38+
39+
Raises
40+
------
41+
FileNotFoundError
42+
If the DLL cannot be found at the constructed or provided path.
43+
OSError
44+
If the DLL fails to load due to an invalid format or missing dependencies.
45+
46+
Notes
47+
-----
48+
- This function is intended for use on Windows platforms.
49+
- The architecture is determined by the running Python interpreter, not the OS alone.
50+
- The default search path assumes a project structure with DLLs in 'bin/x64' or 'bin/x86'.
5551
"""
5652

57-
# Validate mutually exclusive parameters
58-
if hardcoded_dll_location and possible_bin_paths:
59-
raise ValueError("Cannot provide both hardcoded_dll_location and possible_dist_paths.")
60-
6153
# Determine system architecture to pick the correct DLL version
6254
arch = 'x64' if platform.architecture()[0] == '64bit' else 'x86'
6355

6456
# Construct DLL filename based on prefix and architecture suffix
6557
dll_name = f'{dll_prefix_name}_{arch}.dll'
6658

67-
# Base directory is the directory where this script resides
68-
base_dir = os.path.abspath(os.path.dirname(__file__))
69-
70-
if hardcoded_dll_location:
71-
# If hardcoded path is provided, use it after verifying it exists
59+
# If hardcoded path is provided, use it after verifying it exists
60+
if hardcoded_dll_location is None:
61+
dll_path = os.path.abspath(os.path.join(os.path.dirname(__file__), 'bin', arch, dll_name))
62+
elif os.path.isfile(hardcoded_dll_location):
7263
dll_path = os.path.abspath(hardcoded_dll_location)
73-
if not os.path.isfile(dll_path):
74-
raise FileNotFoundError(f"Hardcoded DLL location does not exist: {dll_path}")
7564
else:
76-
# If no hardcoded path, define default search paths if not provided
77-
if possible_bin_paths is None:
78-
# Common fallback directories where DLL might be located relative to this file
79-
possible_bin_paths = [
80-
os.path.join(base_dir, 'bin', arch, dll_name),
81-
os.path.join(base_dir, '..', 'bin', arch, dll_name),
82-
os.path.join(base_dir, '..', '..', 'bin', arch, dll_name),
83-
]
84-
85-
# Convert all candidate paths to absolute paths
86-
abs_paths = [os.path.abspath(p) for p in possible_bin_paths]
87-
88-
# Find the first path where the DLL file actually exists
89-
dll_path = next((p for p in abs_paths if os.path.isfile(p)), None)
90-
91-
# If no valid DLL file was found, raise error with detailed info
92-
if dll_path is None:
93-
raise FileNotFoundError(
94-
f"Could not find {dll_name} DLL in any of the expected locations:\n" +
95-
"\n".join(abs_paths)
96-
)
97-
98-
# Inform user about which DLL path is being loaded
99-
print(f"Loading {dll_path} DLL...")
65+
raise FileNotFoundError(f"Hardcoded DLL location does not exist: {hardcoded_dll_location}")
10066

10167
# Load and return the DLL using the specified loading function
10268
return dll_load_func(dll_path)

tool/compilerHelper.ps1

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ if ($filesToCompile.Count -eq 0) {
4040
}
4141

4242
# --- CLEANUP: Remove .obj/.dll/.lib/.exp in root and move all outputs to bin/{arch} ---
43-
$binRoot = "..\bin"
43+
$binRoot = "..\pyCTools\bin"
4444
$x64Folder = Join-Path $binRoot "x64"
4545
$x86Folder = Join-Path $binRoot "x86"
4646

tool/distributionHelper.ps1

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ try {
1515
# Move one directory up
1616
Set-Location ..
1717

18-
$binPath = Join-Path (Get-Location) "bin"
18+
$binPath = Join-Path (Join-Path (Get-Location) "pyCTools") "bin"
1919

2020
if (-not (Test-Path $binPath -PathType Container)) {
2121
$binPath = Read-Host "bin folder not found. [Have you executed 'compilerHelper.ps1'?] => Please enter the full path to the bin folder"
@@ -89,7 +89,7 @@ try {
8989
Write-Host "bin folder validated successfully."
9090

9191
# Create dist folder if not exists
92-
$distPath = Join-Path (Split-Path $binPath -Parent) "dist"
92+
$distPath = Join-Path (Split-Path (Split-Path $binPath -Parent) -Parent) "dist"
9393
if (-not (Test-Path $distPath)) {
9494
New-Item -Path $distPath -ItemType Directory -ErrorAction Stop | Out-Null
9595
Write-Host "Created dist folder at $distPath"

0 commit comments

Comments
 (0)