pyzmq/buildutils/constants.py

128 lines
3.5 KiB
Python

"""
script for generating files that involve repetitive updates for zmq constants.
Run as `python3 buildutils/constants.py`
Run this after updating utils/constant_names
Currently generates the following files from templates:
- constant_enums.pxi
- constants.pyi
"""
# Copyright (C) PyZMQ Developers
# Distributed under the terms of the Modified BSD License.
import enum
import os
import sys
from subprocess import run
pjoin = os.path.join
buildutils = os.path.abspath(os.path.dirname(__file__))
root = pjoin(buildutils, os.path.pardir)
sys.path.insert(0, pjoin(root, 'zmq'))
import constants # noqa: E402
all_names = []
for name in constants.__all__:
item = getattr(constants, name)
if isinstance(item, enum.Enum):
all_names.append(name)
ifndef_t = """#ifndef {0}
#define {0} (_PYZMQ_UNDEFINED)
#endif
"""
def no_prefix(name):
"""does the given constant have a ZMQ_ prefix?"""
return name.startswith('E') and not name.startswith('EVENT')
def cython_enums():
"""generate `enum: ZMQ_CONST` block for constant_enums.pxi"""
lines = []
for name in all_names:
if no_prefix(name):
lines.append(f'enum: ZMQ_{name} "{name}"')
else:
lines.append(f'enum: ZMQ_{name}')
return dict(ZMQ_ENUMS='\n '.join(lines))
def ifndefs():
"""generate `#ifndef ZMQ_CONST` block for zmq_constants.h"""
lines = ['#define _PYZMQ_UNDEFINED (-9999)']
for name in all_names:
if not no_prefix(name):
name = f'ZMQ_{name}'
lines.append(ifndef_t.format(name))
return dict(ZMQ_IFNDEFS='\n'.join(lines))
def promoted_constants():
"""Generate CONST: int for mypy"""
original_lines = []
with open(constants.__file__) as f:
for line in f.readlines():
original_lines.append(line)
if "AUTOGENERATED_BELOW_HERE" in line:
original_file = "".join(original_lines)
break
else:
raise ValueError("Never encountered AUTOGENERATED_BELOW_HERE")
global_assignments = []
all_lines = ["__all__: list[str] = ["]
for cls_name in sorted(dir(constants)):
if cls_name.startswith("_"):
continue
cls = getattr(constants, cls_name)
if not isinstance(cls, type) or not issubclass(cls, enum.Enum):
continue
get_global_name = getattr(cls, "_global_name", lambda name: name)
all_lines.append(f' "{cls_name}",')
for key in cls.__members__:
global_name = get_global_name(key)
all_lines.append(f' "{global_name}",')
global_assignments.append(f"{global_name}: int = {cls_name}.{key}")
all_lines.append("]")
return dict(
original_file=original_file,
global_assignments="\n".join(global_assignments),
__all__="\n".join(all_lines),
)
def generate_file(fname, ns_func, dest_dir="."):
"""generate a constants file from its template"""
with open(pjoin(root, 'buildutils', 'templates', f'{fname}')) as f:
tpl = f.read()
out = tpl.format(**ns_func())
dest = pjoin(dest_dir, fname)
print(f"generating {dest} from template")
with open(dest, 'w') as f:
f.write(out)
if fname.endswith(".py"):
run(["ruff", "format", dest])
def render_constants():
"""render generated constant files from templates"""
generate_file(
"constant_enums.pxi", cython_enums, pjoin(root, 'zmq', 'backend', 'cython')
)
generate_file("constants.py", promoted_constants, pjoin(root, 'zmq'))
if __name__ == '__main__':
render_constants()