python-undef 0.1.0__py2.py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of python-undef might be problematic. Click here for more details.
python_undef/__init__.py
ADDED
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import re
|
|
3
|
+
import datetime
|
|
4
|
+
import sys
|
|
5
|
+
|
|
6
|
+
def is_valid_macro_name(macro_name):
|
|
7
|
+
"""
|
|
8
|
+
Determine whether a macro name is valid using Python's standard library methods.
|
|
9
|
+
|
|
10
|
+
Args:
|
|
11
|
+
macro_name: The macro name to check.
|
|
12
|
+
|
|
13
|
+
Returns:
|
|
14
|
+
bool: True if it's a valid Python identifier, False otherwise.
|
|
15
|
+
"""
|
|
16
|
+
# Empty string is invalid
|
|
17
|
+
if not macro_name:
|
|
18
|
+
return False
|
|
19
|
+
|
|
20
|
+
# Use str.isidentifier() to check for valid identifier syntax
|
|
21
|
+
return macro_name.isidentifier()
|
|
22
|
+
|
|
23
|
+
def extract_macro_name(line):
|
|
24
|
+
"""Extract the macro name from a #define line (handles spaces between # and define)."""
|
|
25
|
+
line = line.strip()
|
|
26
|
+
|
|
27
|
+
# Match '#', optional spaces, 'define', spaces, and the macro name
|
|
28
|
+
match = re.match(r'^#\s*define\s+([A-Za-z_][A-Za-z0-9_]*)', line)
|
|
29
|
+
if not match:
|
|
30
|
+
return None
|
|
31
|
+
|
|
32
|
+
candidate = match.group(1)
|
|
33
|
+
|
|
34
|
+
# Validate with standard identifier rules
|
|
35
|
+
if candidate and is_valid_macro_name(candidate):
|
|
36
|
+
return candidate
|
|
37
|
+
return None
|
|
38
|
+
|
|
39
|
+
def is_standard_python_macro(macro_name):
|
|
40
|
+
"""
|
|
41
|
+
Check whether a macro follows Python's standard naming conventions.
|
|
42
|
+
Rules: Starts with Py, PY, _Py, _PY, or ends with _H.
|
|
43
|
+
"""
|
|
44
|
+
standard_prefixes = ('Py', 'PY', '_Py', '_PY')
|
|
45
|
+
return macro_name.startswith(standard_prefixes) or macro_name.endswith('_H')
|
|
46
|
+
|
|
47
|
+
def generate_undef_code(macro_name):
|
|
48
|
+
"""Generate the code to undefine a macro."""
|
|
49
|
+
return f"""#ifndef DONOTUNDEF_{macro_name}
|
|
50
|
+
#ifdef {macro_name}
|
|
51
|
+
#undef {macro_name}
|
|
52
|
+
#endif
|
|
53
|
+
#endif
|
|
54
|
+
|
|
55
|
+
"""
|
|
56
|
+
|
|
57
|
+
def generate_python_undef_header(pyconfig_path, output_path=None):
|
|
58
|
+
"""
|
|
59
|
+
Generate the Python_undef.h header file.
|
|
60
|
+
|
|
61
|
+
Args:
|
|
62
|
+
pyconfig_path: Path to pyconfig.h
|
|
63
|
+
output_path: Output file path, defaults to Python_undef.h in the current directory.
|
|
64
|
+
"""
|
|
65
|
+
if output_path is None:
|
|
66
|
+
file_dir = os.path.dirname(os.path.abspath(__file__))
|
|
67
|
+
output_path = f'{file_dir}/include/Python_undef.h'
|
|
68
|
+
if not os.path.exists(f'{file_dir}/include'):
|
|
69
|
+
os.makedirs(f'{file_dir}/include')
|
|
70
|
+
|
|
71
|
+
# Read pyconfig.h
|
|
72
|
+
try:
|
|
73
|
+
with open(pyconfig_path, 'r', encoding='utf-8') as f:
|
|
74
|
+
lines = f.readlines()
|
|
75
|
+
except FileNotFoundError:
|
|
76
|
+
print(f"Error: File not found {pyconfig_path}")
|
|
77
|
+
return False
|
|
78
|
+
except Exception as e:
|
|
79
|
+
print(f"Error reading file: {e}")
|
|
80
|
+
return False
|
|
81
|
+
|
|
82
|
+
# Collect macros
|
|
83
|
+
macros_to_undef = []
|
|
84
|
+
all_macros = []
|
|
85
|
+
invalid_macros = []
|
|
86
|
+
|
|
87
|
+
print("Analyzing pyconfig.h...")
|
|
88
|
+
|
|
89
|
+
for i, line in enumerate(lines, 1):
|
|
90
|
+
macro_name = extract_macro_name(line)
|
|
91
|
+
if macro_name:
|
|
92
|
+
all_macros.append(macro_name)
|
|
93
|
+
|
|
94
|
+
# New rule: any macro not starting with Py/PY/_Py/_PY and not ending with _H is considered non-standard
|
|
95
|
+
if not is_standard_python_macro(macro_name):
|
|
96
|
+
macros_to_undef.append(macro_name)
|
|
97
|
+
print(f"Line {i:4d}: Found non-standard macro '{macro_name}'")
|
|
98
|
+
else:
|
|
99
|
+
# Check if line looks like a define but has invalid name
|
|
100
|
+
line = line.strip()
|
|
101
|
+
if line.startswith('#'):
|
|
102
|
+
m = re.match(r'^#\s*define\s+(\S+)', line)
|
|
103
|
+
if m:
|
|
104
|
+
candidate = m.group(1)
|
|
105
|
+
if candidate and not is_valid_macro_name(candidate):
|
|
106
|
+
invalid_macros.append((i, candidate))
|
|
107
|
+
|
|
108
|
+
# Deduplicate and sort
|
|
109
|
+
macros_to_undef = sorted(set(macros_to_undef))
|
|
110
|
+
|
|
111
|
+
# Header section
|
|
112
|
+
header = f"""/*
|
|
113
|
+
* Python_undef.h - Automatically generated macro undefinition header
|
|
114
|
+
*
|
|
115
|
+
* This file is automatically generated from {os.path.basename(pyconfig_path)}
|
|
116
|
+
* Contains macros that may need to be undefined to avoid conflicts with other libraries.
|
|
117
|
+
*
|
|
118
|
+
* WARNING: This is an automatically generated file. Do not edit manually.
|
|
119
|
+
*
|
|
120
|
+
* Usage:
|
|
121
|
+
* #include <Python.h>
|
|
122
|
+
* #include <Python_undef.h>
|
|
123
|
+
* #include <other_library_headers.h>
|
|
124
|
+
*
|
|
125
|
+
* To preserve specific macros, define before including this header:
|
|
126
|
+
* #define DONOTUNDEF_MACRO_NAME
|
|
127
|
+
*
|
|
128
|
+
* Generation rules:
|
|
129
|
+
* - Macros starting with Py_, PY_, _Py, _PY are preserved (Python standard)
|
|
130
|
+
* - Macros ending with _H are preserved (header guards)
|
|
131
|
+
* - All other macros are undefined
|
|
132
|
+
* - Macro name validation uses Python's standard identifier checking
|
|
133
|
+
*
|
|
134
|
+
* Generated from: {os.path.abspath(pyconfig_path)}
|
|
135
|
+
* Generated at: {datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')}
|
|
136
|
+
* Total valid macros found: {len(all_macros)}
|
|
137
|
+
* Macros to undef: {len(macros_to_undef)}
|
|
138
|
+
* Invalid macro names skipped: {len(invalid_macros)}
|
|
139
|
+
*/
|
|
140
|
+
|
|
141
|
+
#ifndef PYTHON_UNDEF_H
|
|
142
|
+
#define PYTHON_UNDEF_H
|
|
143
|
+
|
|
144
|
+
#ifndef Py_PYTHON_H
|
|
145
|
+
# error "Python_undef.h must be included *after* Python.h"
|
|
146
|
+
#endif
|
|
147
|
+
|
|
148
|
+
/*
|
|
149
|
+
* Platform Note:
|
|
150
|
+
* - The COMPILER macro is primarily defined in pyconfig.h on Windows
|
|
151
|
+
* - Other platforms define compiler info in Python/getcompiler.c
|
|
152
|
+
* - This macro and others can conflict with libraries such as V8
|
|
153
|
+
*/
|
|
154
|
+
|
|
155
|
+
"""
|
|
156
|
+
|
|
157
|
+
# Generate undef code sections
|
|
158
|
+
undef_sections = []
|
|
159
|
+
for macro_name in macros_to_undef:
|
|
160
|
+
undef_sections.append(generate_undef_code(macro_name))
|
|
161
|
+
|
|
162
|
+
# Footer
|
|
163
|
+
footer = """#endif /* PYTHON_UNDEF_H */
|
|
164
|
+
"""
|
|
165
|
+
|
|
166
|
+
# Write output
|
|
167
|
+
try:
|
|
168
|
+
with open(output_path, 'w', encoding='utf-8', newline='\n') as f:
|
|
169
|
+
f.write(header)
|
|
170
|
+
f.writelines(undef_sections)
|
|
171
|
+
f.write(footer)
|
|
172
|
+
|
|
173
|
+
print(f"\n{'='*60}")
|
|
174
|
+
print(f"Successfully generated: {output_path}")
|
|
175
|
+
print(f"{'='*60}")
|
|
176
|
+
print("Summary:")
|
|
177
|
+
print(f" - Total valid macro definitions: {len(all_macros)}")
|
|
178
|
+
print(f" - Macros to undefine: {len(macros_to_undef)}")
|
|
179
|
+
print(f" - Preserved standard macros: {len(all_macros) - len(macros_to_undef)}")
|
|
180
|
+
print(f" - Invalid macro names skipped: {len(invalid_macros)}")
|
|
181
|
+
|
|
182
|
+
if invalid_macros:
|
|
183
|
+
print(f"\nSkipped invalid macro names:")
|
|
184
|
+
for line_num, invalid_macro in invalid_macros[:10]: # show only first 10
|
|
185
|
+
print(f" Line {line_num:4d}: '{invalid_macro}'")
|
|
186
|
+
if len(invalid_macros) > 10:
|
|
187
|
+
print(f" ... and {len(invalid_macros) - 10} more")
|
|
188
|
+
|
|
189
|
+
if macros_to_undef:
|
|
190
|
+
print(f"\nMacros to undefine (first 50):")
|
|
191
|
+
for i, macro in enumerate(macros_to_undef[:50], 1):
|
|
192
|
+
print(f" {i:3d}. {macro}")
|
|
193
|
+
if len(macros_to_undef) > 50:
|
|
194
|
+
print(f" ... and {len(macros_to_undef) - 50} more")
|
|
195
|
+
|
|
196
|
+
print(f"\nUsage Notes:")
|
|
197
|
+
print(f" 1. Include this file before including other library headers.")
|
|
198
|
+
print(f" 2. Use DONOTUNDEF_XXX to protect macros that must be kept.")
|
|
199
|
+
print(f" 3. Regenerate this file whenever rebuilding Python.")
|
|
200
|
+
|
|
201
|
+
return True
|
|
202
|
+
|
|
203
|
+
except Exception as e:
|
|
204
|
+
print(f"Error writing file: {e}")
|
|
205
|
+
return False
|
|
206
|
+
|
|
207
|
+
def main():
|
|
208
|
+
import sysconfig
|
|
209
|
+
from pathlib import Path
|
|
210
|
+
if sys.argv[1:]:
|
|
211
|
+
if sys.argv[1] in ('-h', '--help'):
|
|
212
|
+
print("""Usage:
|
|
213
|
+
python -m python_undef --generate
|
|
214
|
+
Generate Python_undef.h based on the system's pyconfig.h.
|
|
215
|
+
python -m python_undef --include
|
|
216
|
+
Print the include path where Python_undef.h is located.""")
|
|
217
|
+
elif sys.argv[1] == '--generate':
|
|
218
|
+
include_dir = Path(sysconfig.get_path('include'))
|
|
219
|
+
print(f"\n{'='*60}")
|
|
220
|
+
print("Note: Python keywords are not excluded since they are valid macro names in C/C++.")
|
|
221
|
+
print(f"{'='*60}")
|
|
222
|
+
|
|
223
|
+
pyconfig_path = include_dir / "pyconfig.h"
|
|
224
|
+
|
|
225
|
+
if os.path.exists(pyconfig_path):
|
|
226
|
+
success = generate_python_undef_header(pyconfig_path)
|
|
227
|
+
|
|
228
|
+
if success:
|
|
229
|
+
print(f"\n✅ Generation complete!")
|
|
230
|
+
print(f"💡 Tip: Place Python_undef.h inside Python include search path.")
|
|
231
|
+
else:
|
|
232
|
+
print(f"\n❌ Generation failed!")
|
|
233
|
+
|
|
234
|
+
else:
|
|
235
|
+
print(f"File {pyconfig_path} not found.")
|
|
236
|
+
print("Please update the pyconfig_path variable to the actual pyconfig.h path.")
|
|
237
|
+
print("\nTypical paths on Windows:")
|
|
238
|
+
print(" C:\\\\Python3x\\\\include\\\\pyconfig.h")
|
|
239
|
+
print("\nTypical paths on Unix/Linux:")
|
|
240
|
+
print(" /usr/include/python3.x/pyconfig.h")
|
|
241
|
+
print(" /usr/local/include/python3.x/pyconfig.h")
|
|
242
|
+
elif sys.argv[1] == "--include":
|
|
243
|
+
file_dir = os.path.dirname(os.path.abspath(__file__))
|
|
244
|
+
if not Path(file_dir).exists():
|
|
245
|
+
print("File not found. Use 'python -m python_undef --generate' to generate the header first.")
|
|
246
|
+
return
|
|
247
|
+
include_path = os.path.abspath(os.path.join(file_dir, 'include'))
|
|
248
|
+
print(include_path)
|
|
249
|
+
else:
|
|
250
|
+
print("Unknown argument. Use --help for usage information.")
|
|
251
|
+
else:
|
|
252
|
+
print("No arguments provided. Use --help for usage information.")
|
python_undef/__main__.py
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: python_undef
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Python undefined header in pyconfig.h
|
|
5
|
+
Project-URL: homepage, https://github.com/Locked-chess-official/python_undef
|
|
6
|
+
Author-email: Locked-chess-official <13140752715@163.com>
|
|
7
|
+
License: MIT
|
|
8
|
+
License-File: LICENSE
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
python_undef/__init__.py,sha256=OSSRLqTFB7lDghJ70BkgI4LYdkj_D-YTs0XcKDYhKtg,9361
|
|
2
|
+
python_undef/__main__.py,sha256=_P5nfB_sOJcMhh75oVFs_0I6mHNXSw8EYgR6N8KDA-0,60
|
|
3
|
+
python_undef-0.1.0.dist-info/METADATA,sha256=CPkAaC8yJULZz-JmHiYP2lMWxhlF_fd-qnpHjWFvAWk,273
|
|
4
|
+
python_undef-0.1.0.dist-info/WHEEL,sha256=tkmg4JIqwd9H8mL30xA7crRmoStyCtGp0VWshokd1Jc,105
|
|
5
|
+
python_undef-0.1.0.dist-info/licenses/LICENSE,sha256=EKyJBXvuO6DHzy9gUe1HC37yV521nbbAFFWTsPWflm4,1099
|
|
6
|
+
python_undef-0.1.0.dist-info/RECORD,,
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Locked-chess-official
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|