From b377282964ad20a44f29b34608a84d71223e8fbb Mon Sep 17 00:00:00 2001 From: Carl Hjerpe Date: Fri, 12 Apr 2024 16:49:12 +0200 Subject: [PATCH] Implement type stubs for ecodes Helps your $EDITOR find the constants from ecodes.c, making for a more pleasant developer experience. --- evdev/genpyi.py | 58 +++++++++++++++++++++++++++++++++++++++++++++++++ setup.py | 37 +++++++++++++++++++++++++------ 2 files changed, 88 insertions(+), 7 deletions(-) create mode 100755 evdev/genpyi.py diff --git a/evdev/genpyi.py b/evdev/genpyi.py new file mode 100755 index 0000000..a60740d --- /dev/null +++ b/evdev/genpyi.py @@ -0,0 +1,58 @@ +#! /usr/bin/env python3 +""" +Generate a Python extension module with the constants defined in linux/input.h. +""" + +from __future__ import print_function +import os, sys, re + + +# ----------------------------------------------------------------------------- +# The default header file locations to try. +headers = [ + "/usr/include/linux/input.h", + "/usr/include/linux/input-event-codes.h", + "/usr/include/linux/uinput.h", +] + +if sys.argv[1:]: + headers = sys.argv[1:] + +uname = list(os.uname()) +del uname[1] +uname = " ".join(uname) +print(f"# used_linux_headers: {headers}") + + +# ----------------------------------------------------------------------------- +macro_regex = r"#define +((?:KEY|ABS|REL|SW|MSC|LED|BTN|REP|SND|ID|EV|BUS|SYN|FF|UI_FF|INPUT_PROP)_\w+)" +macro_regex = re.compile(macro_regex) + +# ----------------------------------------------------------------------------- +template = rf""" +# Automatically generated by evdev.genecodes +# Generated on {uname} +# Headers: {headers} + +""" + +def parse_header(header): + lines = "" + for line in open(header): + macro = macro_regex.search(line) + if macro: + #lines += f" {macro.group(1)}: int{os.linesep}" + lines += f"{macro.group(1)}: int{os.linesep}" + + return lines + + +for header in headers: + try: + fh = open(header) + except (IOError, OSError): + print(f"Unable to read header file: {header}") + continue + template += f"{parse_header(header)}" + +print(template) diff --git a/setup.py b/setup.py index a776130..b13ddca 100755 --- a/setup.py +++ b/setup.py @@ -5,10 +5,11 @@ from setuptools import setup, Extension, Command from setuptools.command import build_ext as _build_ext - +from setuptools.command import install as _install curdir = Path(__file__).resolve().parent -ecodes_path = curdir / "evdev/ecodes.c" +ecodes_c_path = curdir / "evdev/ecodes.c" +ecodes_pyi_path = curdir / "evdev/ecodes.pyi" def create_ecodes(headers=None): @@ -54,11 +55,16 @@ def create_ecodes(headers=None): from subprocess import run - print("writing %s (using %s)" % (ecodes_path, " ".join(headers))) - with ecodes_path.open("w") as fh: + print("writing %s (using %s)" % (ecodes_c_path, " ".join(headers))) + with ecodes_c_path.open("w") as fh: cmd = [sys.executable, "evdev/genecodes.py", *headers] run(cmd, check=True, stdout=fh) + print("writing %s (using %s)" % (ecodes_pyi_path, " ".join(headers))) + with ecodes_pyi_path.open("w") as fh: + cmd = [sys.executable, "evdev/genpyi.py", *headers] + run(cmd, check=True, stdout=fh) + class build_ecodes(Command): description = "generate ecodes.c" @@ -80,20 +86,36 @@ def run(self): class build_ext(_build_ext.build_ext): def has_ecodes(self): - if ecodes_path.exists(): + if ecodes_c_path.exists(): print("ecodes.c already exists ... skipping build_ecodes") - return not ecodes_path.exists() + return not ecodes_c_path.exists() def run(self): for cmd_name in self.get_sub_commands(): self.run_command(cmd_name) _build_ext.build_ext.run(self) - sub_commands = [("build_ecodes", has_ecodes)] + _build_ext.build_ext.sub_commands + sub_commands = [("build_ecodes", has_ecodes)] + _build_ext.build_ext.sub_commands # type: ignore + use_stubs = True + +# I've been reading through setuptools docs, but i can't for the lofe of me figure out how to bring the pyi stubs into the package "cleanly" +class install(_install.install): + def run(self): + print(os.listdir(os.getenv("src"))) + #_install.install.copy_file(self, "evdev/ecodes.pyi", "evdev/ecodes.pyi") + installdir = "build/lib.linux-x86_64-cpython-311/evdev" + Path(f"{installdir}/ecodes.pyi").write_bytes(Path("evdev/ecodes.pyi").read_bytes()) + print("CUSTOM INSTALLER") + print(self.build_lib) + _install.install.run(self) cflags = ["-std=c99", "-Wno-error=declaration-after-statement"] setup( + #include_package_data=True, + packages=["evdev"], + package_dir={'evdev': 'evdev'}, + package_data={'evdev': ["*.pyi"]}, ext_modules=[ Extension("evdev._input", sources=["evdev/input.c"], extra_compile_args=cflags), Extension("evdev._uinput", sources=["evdev/uinput.c"], extra_compile_args=cflags), @@ -102,5 +124,6 @@ def run(self): cmdclass={ "build_ext": build_ext, "build_ecodes": build_ecodes, + "install": install, }, )