Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package python-evdev for openSUSE:Factory 
checked in at 2025-01-30 14:52:15
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-evdev (Old)
 and      /work/SRC/openSUSE:Factory/.python-evdev.new.2316 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "python-evdev"

Thu Jan 30 14:52:15 2025 rev:16 rq:1241259 version:1.8.0

Changes:
--------
--- /work/SRC/openSUSE:Factory/python-evdev/python-evdev.changes        
2024-05-10 12:06:32.748557642 +0200
+++ /work/SRC/openSUSE:Factory/.python-evdev.new.2316/python-evdev.changes      
2025-01-30 14:52:22.156328810 +0100
@@ -1,0 +2,18 @@
+Wed Jan 29 19:36:45 UTC 2025 - Matthias Bach <ma...@marix.org> - 1.8.0
+
+- Update to 1.8.0
+  * The evdev.ecodes module is now generated at install time and
+    contains only constants. This allows type checking and
+    introspection of the evdev.ecodes module, without having to
+    execute it first. The old module is available as
+    evdev.ecodes_runtime.
+  * Reverse mappings in evdev.ecodes that point to more than one
+    value are now tuples instead of lists.
+  * Fix keyboard delay and repeat being swapped.
+  * Move the syn() convenience method from InputDevice to EventIO.
+- Add fix-tests.patch to pull in test fixes that upstream only
+  included after tagging their release.
+- Switch source download during packaging from disabledrun to
+  manualrun.
+
+-------------------------------------------------------------------

Old:
----
  python-evdev-1.7.1.tar.gz

New:
----
  fix-tests.patch
  python-evdev-1.8.0.tar.gz

BETA DEBUG BEGIN:
  New:  * Move the syn() convenience method from InputDevice to EventIO.
- Add fix-tests.patch to pull in test fixes that upstream only
  included after tagging their release.
BETA DEBUG END:

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ python-evdev.spec ++++++
--- /var/tmp/diff_new_pack.TwN701/_old  2025-01-30 14:52:23.120368575 +0100
+++ /var/tmp/diff_new_pack.TwN701/_new  2025-01-30 14:52:23.124368740 +0100
@@ -1,7 +1,7 @@
 #
 # spec file for package python-evdev
 #
-# Copyright (c) 2024 SUSE LLC
+# Copyright (c) 2025 SUSE LLC
 #
 # All modifications and additions to the file contributed by third parties
 # remain the property of their copyright owners, unless otherwise agreed
@@ -19,7 +19,7 @@
 %define modname evdev
 %{?sle15_python_module_pythons}
 Name:           python-evdev
-Version:        1.7.1
+Version:        1.8.0
 Release:        0
 Summary:        Python bindings to the Linux input handling subsystem
 License:        BSD-3-Clause
@@ -27,6 +27,7 @@
 URL:            https://github.com/gvalkov/python-evdev
 # Source needs to be pulled form Github as the source distribution on PyPI 
lacks the test directory
 Source:         
https://github.com/gvalkov/python-evdev/archive/refs/tags/v%{version}.tar.gz#/python-evdev-%{version}.tar.gz
+Patch0:         fix-tests.patch
 BuildRequires:  %{python_module devel}
 BuildRequires:  %{python_module pip}
 BuildRequires:  %{python_module pytest}

++++++ _service ++++++
--- /var/tmp/diff_new_pack.TwN701/_old  2025-01-30 14:52:23.152369895 +0100
+++ /var/tmp/diff_new_pack.TwN701/_new  2025-01-30 14:52:23.156370060 +0100
@@ -1,4 +1,4 @@
 <services>
-  <service name="download_files" mode="disabled" />
+  <service name="download_files" mode="manual" />
 </services>
 

++++++ fix-tests.patch ++++++
From: Georgi Valkov <georgi.t.val...@gmail.com>
Date: Sat, 25 Jan 2025 18:04:39 +0100
Subject: [PATCH] Fix tests
Upstream: merged

This is pulled from upstream main branch where it was committed right after the 
release.

---
 tests/test_util.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/tests/test_util.py b/tests/test_util.py
index 5a979df..7112927 100644
--- a/tests/test_util.py
+++ b/tests/test_util.py
@@ -6,7 +6,7 @@ def test_match_ecodes_a():
     assert res == {1: [372, 418, 419, 420]}
     assert dict(util.resolve_ecodes_dict(res)) == {
         ("EV_KEY", 1): [
-            (["KEY_FULL_SCREEN", "KEY_ZOOM"], 372),
+            (("KEY_FULL_SCREEN", "KEY_ZOOM"), 372),
             ("KEY_ZOOMIN", 418),
             ("KEY_ZOOMOUT", 419),
             ("KEY_ZOOMRESET", 420),


++++++ python-evdev-1.7.1.tar.gz -> python-evdev-1.8.0.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/python-evdev-1.7.1/.github/workflows/install.yaml 
new/python-evdev-1.8.0/.github/workflows/install.yaml
--- old/python-evdev-1.7.1/.github/workflows/install.yaml       2024-05-08 
01:01:20.000000000 +0200
+++ new/python-evdev-1.8.0/.github/workflows/install.yaml       2025-01-25 
17:37:33.000000000 +0100
@@ -11,10 +11,10 @@
       fail-fast: false
       matrix:
         os: [ubuntu-latest]
-        python-version: ["3.7", "3.8", "3.9", "3.10", "3.11", "3.12"]
+        python-version: ["3.8", "3.9", "3.10", "3.11", "3.12", "3.13"]
         include:
           - os: ubuntu-latest
-            python-version: "3.7"
+            python-version: "3.8"
 
     steps:
       - uses: actions/checkout@v4
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/python-evdev-1.7.1/.github/workflows/lint.yml 
new/python-evdev-1.8.0/.github/workflows/lint.yml
--- old/python-evdev-1.7.1/.github/workflows/lint.yml   1970-01-01 
01:00:00.000000000 +0100
+++ new/python-evdev-1.8.0/.github/workflows/lint.yml   2025-01-25 
17:37:33.000000000 +0100
@@ -0,0 +1,27 @@
+name: Lint
+
+on:
+  - push
+  - pull_request
+
+jobs:
+  pylint:
+    runs-on: ${{ matrix.os }}
+    strategy:
+      fail-fast: false
+      matrix:
+        os: [ubuntu-latest]
+        python-version: ["3.12"]
+
+    steps:
+      - uses: actions/checkout@v4
+      - name: Set up Python ${{ matrix.python-version }}
+        uses: actions/setup-python@v5
+        with:
+          python-version: ${{ matrix.python-version }}
+
+      - name: Check for pylint errors
+        run: |
+          python -m pip install pylint setuptools
+          python setup.py build
+          python -m pylint --verbose -E build/lib*/evdev
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/python-evdev-1.7.1/.github/workflows/test.yml 
new/python-evdev-1.8.0/.github/workflows/test.yml
--- old/python-evdev-1.7.1/.github/workflows/test.yml   1970-01-01 
01:00:00.000000000 +0100
+++ new/python-evdev-1.8.0/.github/workflows/test.yml   2025-01-25 
17:37:33.000000000 +0100
@@ -0,0 +1,29 @@
+name: Test
+
+on:
+  - push
+  - pull_request
+
+jobs:
+  test:
+    runs-on: ${{ matrix.os }}
+    strategy:
+      fail-fast: false
+      matrix:
+        os: [ubuntu-latest]
+        python-version: ["3.12"]
+
+    steps:
+      - uses: actions/checkout@v4
+      - name: Set up Python ${{ matrix.python-version }}
+        uses: actions/setup-python@v5
+        with:
+          python-version: ${{ matrix.python-version }}
+
+      - name: Run pytest tests
+        # pip install -e . builds _ecodes and such into the evdev directory
+        # sudo required to write to uinputs
+        run: |
+          sudo python -m pip install pytest setuptools
+          sudo python -m pip install -e .
+          sudo python -m pytest tests
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/python-evdev-1.7.1/.gitignore 
new/python-evdev-1.8.0/.gitignore
--- old/python-evdev-1.7.1/.gitignore   2024-05-08 01:01:20.000000000 +0200
+++ new/python-evdev-1.8.0/.gitignore   2025-01-25 17:37:33.000000000 +0100
@@ -5,20 +5,22 @@
 develop-eggs/
 dist/
 build/
+wheelhouse/
 dropin.cache
 pip-log.txt
 .installed.cfg
 .coverage
 tags
 TAGS
-evdev/*.so
-evdev/ecodes.c
-evdev/iprops.c
-docs/_build
 .#*
 __pycache__
 .pytest_cache
+.ruff_cache
 
+evdev/*.so
+evdev/ecodes.c
+evdev/ecodes.pyi
+docs/_build
 evdev/_ecodes.py
 evdev/_input.py
 evdev/_uinput.py
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/python-evdev-1.7.1/LICENSE 
new/python-evdev-1.8.0/LICENSE
--- old/python-evdev-1.7.1/LICENSE      2024-05-08 01:01:20.000000000 +0200
+++ new/python-evdev-1.8.0/LICENSE      2025-01-25 17:37:33.000000000 +0100
@@ -1,4 +1,4 @@
-Copyright (c) 2012-2023 Georgi Valkov. All rights reserved.
+Copyright (c) 2012-2025 Georgi Valkov. All rights reserved.
 
 Redistribution and use in source and binary forms, with or without
 modification, are permitted provided that the following conditions are
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/python-evdev-1.7.1/MANIFEST.in 
new/python-evdev-1.8.0/MANIFEST.in
--- old/python-evdev-1.7.1/MANIFEST.in  2024-05-08 01:01:20.000000000 +0200
+++ new/python-evdev-1.8.0/MANIFEST.in  2025-01-25 17:37:33.000000000 +0100
@@ -2,3 +2,4 @@
 # evdev headers of the running kernel. Refer to the 'build_ecodes' distutils
 # command in setup.py.
 exclude evdev/ecodes.c
+include evdev/ecodes.py
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/python-evdev-1.7.1/README.md 
new/python-evdev-1.8.0/README.md
--- old/python-evdev-1.7.1/README.md    2024-05-08 01:01:20.000000000 +0200
+++ new/python-evdev-1.8.0/README.md    2025-01-25 17:37:33.000000000 +0100
@@ -3,6 +3,7 @@
 <p>
     <a href="https://pypi.python.org/pypi/evdev";><img alt="pypi version" 
src="https://img.shields.io/pypi/v/evdev.svg";></a>
     <a href="https://github.com/gvalkov/python-evdev/blob/main/LICENSE";><img 
alt="License" src="https://img.shields.io/pypi/l/evdev";></a>
+    <a href="https://repology.org/project/python:evdev/versions";><img 
alt="Packaging status" 
src="https://repology.org/badge/tiny-repos/python:evdev.svg";></a>
 </p>
 
 This package provides bindings to the generic input event interface in Linux.
Binary files old/python-evdev-1.7.1/docs/_static/evdev-logo-small.png and 
new/python-evdev-1.8.0/docs/_static/evdev-logo-small.png differ
Binary files old/python-evdev-1.7.1/docs/_static/evdev-logo.png and 
new/python-evdev-1.8.0/docs/_static/evdev-logo.png differ
Binary files old/python-evdev-1.7.1/docs/_static/github-logo.png and 
new/python-evdev-1.8.0/docs/_static/github-logo.png differ
Binary files 
old/python-evdev-1.7.1/docs/_static/pacifica-icon-set/distributor-logo-archlinux.png
 and 
new/python-evdev-1.8.0/docs/_static/pacifica-icon-set/distributor-logo-archlinux.png
 differ
Binary files 
old/python-evdev-1.7.1/docs/_static/pacifica-icon-set/distributor-logo-debian.png
 and 
new/python-evdev-1.8.0/docs/_static/pacifica-icon-set/distributor-logo-debian.png
 differ
Binary files 
old/python-evdev-1.7.1/docs/_static/pacifica-icon-set/distributor-logo-fedora.png
 and 
new/python-evdev-1.8.0/docs/_static/pacifica-icon-set/distributor-logo-fedora.png
 differ
Binary files 
old/python-evdev-1.7.1/docs/_static/pacifica-icon-set/distributor-logo-linux-mint.png
 and 
new/python-evdev-1.8.0/docs/_static/pacifica-icon-set/distributor-logo-linux-mint.png
 differ
Binary files 
old/python-evdev-1.7.1/docs/_static/pacifica-icon-set/distributor-logo-opensuse.png
 and 
new/python-evdev-1.8.0/docs/_static/pacifica-icon-set/distributor-logo-opensuse.png
 differ
Binary files 
old/python-evdev-1.7.1/docs/_static/pacifica-icon-set/distributor-logo-raspbian.png
 and 
new/python-evdev-1.8.0/docs/_static/pacifica-icon-set/distributor-logo-raspbian.png
 differ
Binary files 
old/python-evdev-1.7.1/docs/_static/pacifica-icon-set/distributor-logo-ubuntu.png
 and 
new/python-evdev-1.8.0/docs/_static/pacifica-icon-set/distributor-logo-ubuntu.png
 differ
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/python-evdev-1.7.1/docs/changelog.rst 
new/python-evdev-1.8.0/docs/changelog.rst
--- old/python-evdev-1.7.1/docs/changelog.rst   2024-05-08 01:01:20.000000000 
+0200
+++ new/python-evdev-1.8.0/docs/changelog.rst   2025-01-25 17:37:33.000000000 
+0100
@@ -1,6 +1,29 @@
 Changelog
 ---------
 
+1.8.0 (Jan 25, 2025)
+==================
+
+- Binary wheels are now provided by the `evdev-binary 
http://pypi.python.org/pypi/evdev-binary`_ package.
+  The package is compiled on manylinux_2_28 against kernel 4.18.
+
+- The ``evdev.ecodes`` module is now generated at install time and contains 
only constants. This allows type
+  checking and introspection of the ``evdev.ecodes`` module, without having to 
execute it first. The old
+  module is available as ``evdev.ecodes_runtime``. In case generation of the 
static ``ecodes.py`` fails, the
+  install process falls back to using ``ecodes_runtime.py`` as ``ecodes.py``.
+
+- Reverse mappings in ``evdev.ecodes`` that point to more than one value are 
now tuples and not lists. For example::
+
+    >>> ecodes.KEY[153]
+    153: ('KEY_DIRECTION', 'KEY_ROTATE_DISPLAY'),
+
+- Minimum Python version raised to Python 3.8.
+
+- Fix keyboard delay and repeat being swapped.
+
+- Move `syn()` convenience method from `InputDevice` to `EventIO`.
+
+
 1.7.1 (May 8, 2024)
 ====================
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/python-evdev-1.7.1/docs/conf.py 
new/python-evdev-1.8.0/docs/conf.py
--- old/python-evdev-1.7.1/docs/conf.py 2024-05-08 01:01:20.000000000 +0200
+++ new/python-evdev-1.8.0/docs/conf.py 2025-01-25 17:37:33.000000000 +0100
@@ -67,7 +67,7 @@
 # built documents.
 #
 # The full version, including alpha/beta/rc tags.
-release = "1.7.1"
+release = "1.8.0"
 
 # The short X.Y version.
 version = release
@@ -130,7 +130,7 @@
 
 # The name of an image file (relative to this directory) to place at the top
 # of the sidebar.
-# html_logo = '_static/evdev-logo-small.png'
+# html_logo = ''
 
 # The name of an image file (within the static path) to use as favicon of the
 # docs.  This file should be a Windows icon file (.ico) being 16x16 or 32x32
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/python-evdev-1.7.1/docs/install.rst 
new/python-evdev-1.8.0/docs/install.rst
--- old/python-evdev-1.7.1/docs/install.rst     2024-05-08 01:01:20.000000000 
+0200
+++ new/python-evdev-1.8.0/docs/install.rst     2025-01-25 17:37:33.000000000 
+0100
@@ -1,31 +1,13 @@
-From a binary package
-=====================
-
-Python-evdev has been packaged for the following GNU/Linux distributions:
+From an OS package
+==================
 
+Python-evdev has been packaged for the following distributions:
 
 .. raw:: html
 
-    <div style="margin:1em;">
-    <a href="https://www.archlinux.org/packages/extra/x86_64/python-evdev/";>
-      <img height="30px" 
src="_static/pacifica-icon-set/distributor-logo-archlinux.png">
-    </a>
-    <a 
href="https://packages.debian.org/search?searchon=names&keywords=python-evdev";>
-      <img height="30px" 
src="_static/pacifica-icon-set/distributor-logo-debian.png">
+    <a href="https://repology.org/project/python:evdev/versions";>
+      <img 
src="https://repology.org/badge/vertical-allrepos/python:evdev.svg?exclude_sources=modules,site&exclude_unsupported=1";
 alt="Packaging status">
     </a>
-    <a 
href="https://packages.ubuntu.com/search?suite=default&section=all&arch=any&keywords=python-evdev&searchon=names";>
-      <img height="30px" 
src="_static/pacifica-icon-set/distributor-logo-ubuntu.png">
-    </a>
-    <a 
href="https://packages.fedoraproject.org/pkgs/python-evdev/python3-evdev/";>
-      <img height="30px" 
src="_static/pacifica-icon-set/distributor-logo-fedora.png">
-    </a>
-    <!--
-    <a href=""><img height="40px" 
src="_static/pacifica-icon-set/distributor-logo-raspbian.png"></a>
-    <a href=""><img height="40px" 
src="_static/pacifica-icon-set/distributor-logo-debian.png"></a>
-    <a href=""><img height="40px" 
src="_static/pacifica-icon-set/distributor-logo-linux-mint.png"></a>
-    <a href=""><img height="40px" 
src="_static/pacifica-icon-set/distributor-logo-opensuse.png"></a>
-    --!>
-    </div>
 
 Consult the documentation of your OS package manager for installation 
instructions.
 
@@ -36,7 +18,7 @@
 The latest stable version of *python-evdev* can be installed from pypi_,
 provided that you have a compiler, pip_ and the Python and Linux development
 headers installed on your system. Installing these is distribution specific and
-typically falls in one of the following:
+typically falls into one of the following:
 
 On a Debian compatible OS:
 
@@ -58,22 +40,31 @@
 
     $ pacman -S core/linux-api-headers python-pip gcc
 
-Once all dependencies are available, you may install *python-evdev* using pip_:
+Once all OS dependencies are available, you may install *python-evdev* using
+pip_, preferably in a [virtualenv]_:
 
 .. code-block:: bash
 
-    $ sudo pip install evdev    # available globally
-    $ pip install --user evdev  # available to the current user
+    # Install globally (not recommended).
+    $ sudo python3 -m pip install evdev
+
+    # Install for the current user.
+    $ python3 -m pip install --user evdev
+
+    # Install in a virtual environment.
+    $ python3 -m venv abc
+    $ source abc/bin/activate
+    $ python3 -m pip install evdev
 
 
 Specifying header locations
-===========================
+---------------------------
 
 By default, the setup script will look for the ``input.h`` and
 ``input-event-codes.h`` [#f1]_ header files ``/usr/include/linux``.
 
 You may use the ``--evdev-headers`` option to the ``build_ext`` setuptools
-command to  the location of these header files. It accepts one or more
+command to the location of these header files. It accepts one or more
 colon-separated paths. For example:
 
 .. code-block:: bash
@@ -83,11 +74,27 @@
         --include-dirs  buildroot/ \
         install  # or any other command (e.g. develop, bdist, bdist_wheel)
 
-.. [#f1] ``input-event-codes.h`` is found only in more recent kernel versions.
+
+From a binary package
+=====================
+
+You may choose to install a precompiled version of *python-evdev* from pypi. 
The
+`evdev-binary`_ package provides binary wheels that have been compiled on EL8
+against the 4.18.0 kernel headers.
+
+.. code-block:: bash
+
+    $ python3 -m pip install evdev-binary
+
+While the evdev interface is stable, the precompiled version may not be fully
+compatible or expose all the features of your running kernel. For best results,
+it is recommended to use an OS package or to install from source.
 
 
+.. [#f1] ``input-event-codes.h`` is found only in recent kernel versions.
 .. _pypi:              http://pypi.python.org/pypi/evdev
+.. _evdev-binary:      http://pypi.python.org/pypi/evdev-binary
 .. _github:            https://github.com/gvalkov/python-evdev
 .. _pip:               http://pip.readthedocs.org/en/latest/installing.html
 .. _example:           
https://github.com/gvalkov/python-evdev/tree/master/examples
-.. _`async/await`:     https://docs.python.org/3/library/asyncio-task.html
+.. _virtualenv:        https://docs.python.org/3/library/venv.html
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/python-evdev-1.7.1/docs/tutorial.rst 
new/python-evdev-1.8.0/docs/tutorial.rst
--- old/python-evdev-1.7.1/docs/tutorial.rst    2024-05-08 01:01:20.000000000 
+0200
+++ new/python-evdev-1.8.0/docs/tutorial.rst    2025-01-25 17:37:33.000000000 
+0100
@@ -451,9 +451,10 @@
     repeat_count = 1
     effect_id = dev.upload_effect(effect)
     dev.write(ecodes.EV_FF, effect_id, repeat_count)
-    time.sleep(duration_ms)
+    time.sleep(duration_ms / 1000) 
     dev.erase_effect(effect_id)
 
+
 Forwarding force-feedback from uinput to a real device
 ======================================================
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/python-evdev-1.7.1/evdev/__init__.py 
new/python-evdev-1.8.0/evdev/__init__.py
--- old/python-evdev-1.7.1/evdev/__init__.py    2024-05-08 01:01:20.000000000 
+0200
+++ new/python-evdev-1.8.0/evdev/__init__.py    2025-01-25 17:37:33.000000000 
+0100
@@ -2,9 +2,8 @@
 # Gather everything into a single, convenient namespace.
 # --------------------------------------------------------------------------
 
-from evdev.device import DeviceInfo, InputDevice, AbsInfo, EvdevError
-from evdev.events import InputEvent, KeyEvent, RelEvent, SynEvent, AbsEvent, 
event_factory
-from evdev.uinput import UInput, UInputError
-from evdev.util import list_devices, categorize, resolve_ecodes, 
resolve_ecodes_dict
-from evdev import ecodes
-from evdev import ff
+from . import ecodes, ff
+from .device import AbsInfo, DeviceInfo, EvdevError, InputDevice
+from .events import AbsEvent, InputEvent, KeyEvent, RelEvent, SynEvent, 
event_factory
+from .uinput import UInput, UInputError
+from .util import categorize, list_devices, resolve_ecodes, resolve_ecodes_dict
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/python-evdev-1.7.1/evdev/device.py 
new/python-evdev-1.8.0/evdev/device.py
--- old/python-evdev-1.7.1/evdev/device.py      2024-05-08 01:01:20.000000000 
+0200
+++ new/python-evdev-1.8.0/evdev/device.py      2025-01-25 17:37:33.000000000 
+0100
@@ -1,23 +1,22 @@
 # encoding: utf-8
 
+import collections
+import contextlib
 import os
 import warnings
-import contextlib
-import collections
 
-from evdev import _input, ecodes, util
-from evdev.events import InputEvent
+from . import _input, ecodes, util
 
 try:
-    from evdev.eventio_async import EventIO, EvdevError
+    from .eventio_async import EvdevError, EventIO
 except ImportError:
-    from evdev.eventio import EventIO, EvdevError
+    from .eventio import EvdevError, EventIO
 
 
 # --------------------------------------------------------------------------
 _AbsInfo = collections.namedtuple("AbsInfo", ["value", "min", "max", "fuzz", 
"flat", "resolution"])
 
-_KbdInfo = collections.namedtuple("KbdInfo", ["repeat", "delay"])
+_KbdInfo = collections.namedtuple("KbdInfo", ["delay", "repeat"])
 
 _DeviceInfo = collections.namedtuple("DeviceInfo", ["bustype", "vendor", 
"product", "version"])
 
@@ -70,16 +69,16 @@
 
     Attributes
     ----------
-    repeat
-      Keyboard repeat rate in characters per second.
-
     delay
       Amount of time that a key must be depressed before it will start
       to repeat (in milliseconds).
+
+    repeat
+      Keyboard repeat rate in characters per second.
     """
 
     def __str__(self):
-        return "repeat {}, delay {}".format(*self)
+        return "delay {}, repeat {}".format(*self)
 
 
 class DeviceInfo(_DeviceInfo):
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/python-evdev-1.7.1/evdev/ecodes.py 
new/python-evdev-1.8.0/evdev/ecodes.py
--- old/python-evdev-1.7.1/evdev/ecodes.py      2024-05-08 01:01:20.000000000 
+0200
+++ new/python-evdev-1.8.0/evdev/ecodes.py      2025-01-25 17:37:33.000000000 
+0100
@@ -1,101 +1,5 @@
-"""
-This modules exposes the integer constants defined in ``linux/input.h`` and
-``linux/input-event-codes.h``.
+# When installed, this module is replaced by an ecodes.py generated at 
+# build time by genecodes_py.py (see build_ext in setup.py).
 
-Exposed constants::
-
-    KEY, ABS, REL, SW, MSC, LED, BTN, REP, SND, ID, EV,
-    BUS, SYN, FF, FF_STATUS, INPUT_PROP
-
-This module also provides reverse and forward mappings of the names and values
-of the above mentioned constants::
-
-    >>> evdev.ecodes.KEY_A
-    30
-
-    >>> evdev.ecodes.ecodes['KEY_A']
-    30
-
-    >>> evdev.ecodes.KEY[30]
-    'KEY_A'
-
-    >>> evdev.ecodes.REL[0]
-    'REL_X'
-
-    >>> evdev.ecodes.EV[evdev.ecodes.EV_KEY]
-    'EV_KEY'
-
-    >>> evdev.ecodes.bytype[evdev.ecodes.EV_REL][0]
-    'REL_X'
-
-Keep in mind that values in reverse mappings may point to one or more event
-codes. For example::
-
-    >>> evdev.ecodes.FF[80]
-    ['FF_EFFECT_MIN', 'FF_RUMBLE']
-
-    >>> evdev.ecodes.FF[81]
-    'FF_PERIODIC'
-"""
-
-from inspect import getmembers
-from evdev import _ecodes
-
-
-#: Mapping of names to values.
-ecodes = {}
-
-prefixes = "KEY ABS REL SW MSC LED BTN REP SND ID EV BUS SYN FF_STATUS FF 
INPUT_PROP"
-prev_prefix = ""
-g = globals()
-
-# eg. code: 'REL_Z', val: 2
-for code, val in getmembers(_ecodes):
-    for prefix in prefixes.split():  # eg. 'REL'
-        if code.startswith(prefix):
-            ecodes[code] = val
-            # FF_STATUS codes should not appear in the FF reverse mapping
-            if not code.startswith(prev_prefix):
-                d = g.setdefault(prefix, {})
-                # codes that share the same value will be added to a list. eg:
-                # >>> ecodes.FF_STATUS
-                # {0: 'FF_STATUS_STOPPED', 1: ['FF_STATUS_MAX', 
'FF_STATUS_PLAYING']}
-                if val in d:
-                    if isinstance(d[val], list):
-                        d[val].append(code)
-                    else:
-                        d[val] = [d[val], code]
-                else:
-                    d[val] = code
-
-        prev_prefix = prefix
-
-#: Keys are a combination of all BTN and KEY codes.
-keys = {}
-keys.update(BTN)
-keys.update(KEY)
-
-# make keys safe to use for the default list of uinput device
-# capabilities
-del keys[_ecodes.KEY_MAX]
-del keys[_ecodes.KEY_CNT]
-
-#: Mapping of event types to other value/name mappings.
-bytype = {
-    _ecodes.EV_KEY: keys,
-    _ecodes.EV_ABS: ABS,
-    _ecodes.EV_REL: REL,
-    _ecodes.EV_SW: SW,
-    _ecodes.EV_MSC: MSC,
-    _ecodes.EV_LED: LED,
-    _ecodes.EV_REP: REP,
-    _ecodes.EV_SND: SND,
-    _ecodes.EV_SYN: SYN,
-    _ecodes.EV_FF: FF,
-    _ecodes.EV_FF_STATUS: FF_STATUS,
-}
-
-from evdev._ecodes import *
-
-# cheaper than whitelisting in an __all__
-del code, val, prefix, getmembers, g, d, prefixes, prev_prefix
+# This stub exists to make development of evdev itself more convenient. 
+from . ecodes_runtime import *
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/python-evdev-1.7.1/evdev/ecodes_runtime.py 
new/python-evdev-1.8.0/evdev/ecodes_runtime.py
--- old/python-evdev-1.7.1/evdev/ecodes_runtime.py      1970-01-01 
01:00:00.000000000 +0100
+++ new/python-evdev-1.8.0/evdev/ecodes_runtime.py      2025-01-25 
17:37:33.000000000 +0100
@@ -0,0 +1,111 @@
+# pylint: disable=undefined-variable
+"""
+This modules exposes the integer constants defined in ``linux/input.h`` and
+``linux/input-event-codes.h``.
+
+Exposed constants::
+
+    KEY, ABS, REL, SW, MSC, LED, BTN, REP, SND, ID, EV,
+    BUS, SYN, FF, FF_STATUS, INPUT_PROP
+
+This module also provides reverse and forward mappings of the names and values
+of the above mentioned constants::
+
+    >>> evdev.ecodes.KEY_A
+    30
+
+    >>> evdev.ecodes.ecodes['KEY_A']
+    30
+
+    >>> evdev.ecodes.KEY[30]
+    'KEY_A'
+
+    >>> evdev.ecodes.REL[0]
+    'REL_X'
+
+    >>> evdev.ecodes.EV[evdev.ecodes.EV_KEY]
+    'EV_KEY'
+
+    >>> evdev.ecodes.bytype[evdev.ecodes.EV_REL][0]
+    'REL_X'
+
+Keep in mind that values in reverse mappings may point to one or more event
+codes. For example::
+
+    >>> evdev.ecodes.FF[80]
+    ('FF_EFFECT_MIN', 'FF_RUMBLE')
+
+    >>> evdev.ecodes.FF[81]
+    'FF_PERIODIC'
+"""
+
+from inspect import getmembers
+
+from . import _ecodes
+
+#: Mapping of names to values.
+ecodes = {}
+
+prefixes = "KEY ABS REL SW MSC LED BTN REP SND ID EV BUS SYN FF_STATUS FF 
INPUT_PROP".split()
+prev_prefix = ""
+g = globals()
+
+# eg. code: 'REL_Z', val: 2
+for code, val in getmembers(_ecodes):
+    for prefix in prefixes:  # eg. 'REL'
+        if code.startswith(prefix):
+            ecodes[code] = val
+            # FF_STATUS codes should not appear in the FF reverse mapping
+            if not code.startswith(prev_prefix):
+                d = g.setdefault(prefix, {})
+                # codes that share the same value will be added to a list. eg:
+                # >>> ecodes.FF_STATUS
+                # {0: 'FF_STATUS_STOPPED', 1: ['FF_STATUS_MAX', 
'FF_STATUS_PLAYING']}
+                if val in d:
+                    if isinstance(d[val], list):
+                        d[val].append(code)
+                    else:
+                        d[val] = [d[val], code]
+                else:
+                    d[val] = code
+
+        prev_prefix = prefix
+
+
+# Convert lists to tuples.
+k, v = None, None
+for prefix in prefixes:
+    for k, v in g[prefix].items():
+        if isinstance(v, list):
+            g[prefix][k] = tuple(v)
+
+
+#: Keys are a combination of all BTN and KEY codes.
+keys = {}
+keys.update(BTN)
+keys.update(KEY)
+
+# make keys safe to use for the default list of uinput device
+# capabilities
+del keys[_ecodes.KEY_MAX]
+del keys[_ecodes.KEY_CNT]
+
+#: Mapping of event types to other value/name mappings.
+bytype = {
+    _ecodes.EV_KEY: keys,
+    _ecodes.EV_ABS: ABS,
+    _ecodes.EV_REL: REL,
+    _ecodes.EV_SW: SW,
+    _ecodes.EV_MSC: MSC,
+    _ecodes.EV_LED: LED,
+    _ecodes.EV_REP: REP,
+    _ecodes.EV_SND: SND,
+    _ecodes.EV_SYN: SYN,
+    _ecodes.EV_FF: FF,
+    _ecodes.EV_FF_STATUS: FF_STATUS,
+}
+
+from evdev._ecodes import *
+
+# cheaper than whitelisting in an __all__
+del code, val, prefix, getmembers, g, d, k, v, prefixes, prev_prefix
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/python-evdev-1.7.1/evdev/eventio.py 
new/python-evdev-1.8.0/evdev/eventio.py
--- old/python-evdev-1.7.1/evdev/eventio.py     2024-05-08 01:01:20.000000000 
+0200
+++ new/python-evdev-1.8.0/evdev/eventio.py     2025-01-25 17:37:33.000000000 
+0100
@@ -1,10 +1,10 @@
-import os
 import fcntl
-import select
 import functools
+import os
+import select
 
-from evdev import _input, _uinput, ecodes, util
-from evdev.events import InputEvent
+from . import _input, _uinput, ecodes
+from .events import InputEvent
 
 
 # --------------------------------------------------------------------------
@@ -72,6 +72,7 @@
         for event in events:
             yield InputEvent(*event)
 
+    # pylint: disable=no-self-argument
     def need_write(func):
         """
         Decorator that raises :class:`EvdevError` if there is no write access 
to the
@@ -82,6 +83,7 @@
         def wrapper(*args):
             fd = args[0].fd
             if fcntl.fcntl(fd, fcntl.F_GETFL) & os.O_RDWR:
+                # pylint: disable=not-callable
                 return func(*args)
             msg = 'no write access to device "%s"' % args[0].path
             raise EvdevError(msg)
@@ -136,5 +138,14 @@
 
         _uinput.write(self.fd, etype, code, value)
 
+    def syn(self):
+        """
+        Inject a ``SYN_REPORT`` event into the input subsystem. Events
+        queued by :func:`write()` will be fired. If possible, events
+        will be merged into an 'atomic' event.
+        """
+
+        self.write(ecodes.EV_SYN, ecodes.SYN_REPORT, 0)
+
     def close(self):
         pass
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/python-evdev-1.7.1/evdev/eventio_async.py 
new/python-evdev-1.8.0/evdev/eventio_async.py
--- old/python-evdev-1.7.1/evdev/eventio_async.py       2024-05-08 
01:01:20.000000000 +0200
+++ new/python-evdev-1.8.0/evdev/eventio_async.py       2025-01-25 
17:37:33.000000000 +0100
@@ -1,10 +1,10 @@
 import asyncio
 import select
 
-from evdev import eventio
+from . import eventio
 
 # needed for compatibility
-from evdev.eventio import EvdevError
+from .eventio import EvdevError
 
 
 class EventIO(eventio.EventIO):
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/python-evdev-1.7.1/evdev/events.py 
new/python-evdev-1.8.0/evdev/events.py
--- old/python-evdev-1.7.1/evdev/events.py      2024-05-08 01:01:20.000000000 
+0200
+++ new/python-evdev-1.8.0/evdev/events.py      2025-01-25 17:37:33.000000000 
+0100
@@ -37,7 +37,8 @@
 # event type descriptions have been taken mot-a-mot from:
 # http://www.kernel.org/doc/Documentation/input/event-codes.txt
 
-from evdev.ecodes import keys, KEY, SYN, REL, ABS, EV_KEY, EV_REL, EV_ABS, 
EV_SYN
+# pylint: disable=no-name-in-module
+from .ecodes import ABS, EV_ABS, EV_KEY, EV_REL, EV_SYN, KEY, REL, SYN, keys
 
 
 class InputEvent:
@@ -65,13 +66,13 @@
         """Return event timestamp as a float."""
         return self.sec + (self.usec / 1000000.0)
 
-    def __str__(s):
+    def __str__(self):
         msg = "event at {:f}, code {:02d}, type {:02d}, val {:02d}"
-        return msg.format(s.timestamp(), s.code, s.type, s.value)
+        return msg.format(self.timestamp(), self.code, self.type, self.value)
 
-    def __repr__(s):
+    def __repr__(self):
         msg = "{}({!r}, {!r}, {!r}, {!r}, {!r})"
-        return msg.format(s.__class__.__name__, s.sec, s.usec, s.type, s.code, 
s.value)
+        return msg.format(self.__class__.__name__, self.sec, self.usec, 
self.type, self.code, self.value)
 
 
 class KeyEvent:
@@ -119,8 +120,8 @@
         msg = "key event at {:f}, {} ({}), {}"
         return msg.format(self.event.timestamp(), self.scancode, self.keycode, 
ks)
 
-    def __repr__(s):
-        return "{}({!r})".format(s.__class__.__name__, s.event)
+    def __repr__(self):
+        return "{}({!r})".format(self.__class__.__name__, self.event)
 
 
 class RelEvent:
@@ -136,8 +137,8 @@
         msg = "relative axis event at {:f}, {}"
         return msg.format(self.event.timestamp(), REL[self.event.code])
 
-    def __repr__(s):
-        return "{}({!r})".format(s.__class__.__name__, s.event)
+    def __repr__(self):
+        return "{}({!r})".format(self.__class__.__name__, self.event)
 
 
 class AbsEvent:
@@ -153,8 +154,8 @@
         msg = "absolute axis event at {:f}, {}"
         return msg.format(self.event.timestamp(), ABS[self.event.code])
 
-    def __repr__(s):
-        return "{}({!r})".format(s.__class__.__name__, s.event)
+    def __repr__(self):
+        return "{}({!r})".format(self.__class__.__name__, self.event)
 
 
 class SynEvent:
@@ -173,8 +174,8 @@
         msg = "synchronization event at {:f}, {}"
         return msg.format(self.event.timestamp(), SYN[self.event.code])
 
-    def __repr__(s):
-        return "{}({!r})".format(s.__class__.__name__, s.event)
+    def __repr__(self):
+        return "{}({!r})".format(self.__class__.__name__, self.event)
 
 
 #: A mapping of event types to :class:`InputEvent` sub-classes. Used
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/python-evdev-1.7.1/evdev/evtest.py 
new/python-evdev-1.8.0/evdev/evtest.py
--- old/python-evdev-1.7.1/evdev/evtest.py      2024-05-08 01:01:20.000000000 
+0200
+++ new/python-evdev-1.8.0/evdev/evtest.py      2025-01-25 17:37:33.000000000 
+0100
@@ -16,21 +16,15 @@
   evtest /dev/input/event0 /dev/input/event1
 """
 
-from __future__ import print_function
 
+import atexit
+import optparse
 import re
-import sys
 import select
-import atexit
+import sys
 import termios
-import optparse
-
-try:
-    input = raw_input
-except NameError:
-    pass
 
-from evdev import ecodes, list_devices, AbsInfo, InputDevice
+from . import AbsInfo, InputDevice, ecodes, list_devices
 
 
 def parseopt():
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/python-evdev-1.7.1/evdev/ff.py 
new/python-evdev-1.8.0/evdev/ff.py
--- old/python-evdev-1.7.1/evdev/ff.py  2024-05-08 01:01:20.000000000 +0200
+++ new/python-evdev-1.8.0/evdev/ff.py  2025-01-25 17:37:33.000000000 +0100
@@ -1,6 +1,6 @@
 import ctypes
-from evdev import ecodes
 
+from . import ecodes
 
 _u8 = ctypes.c_uint8
 _u16 = ctypes.c_uint16
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/python-evdev-1.7.1/evdev/genecodes.py 
new/python-evdev-1.8.0/evdev/genecodes.py
--- old/python-evdev-1.7.1/evdev/genecodes.py   2024-05-08 01:01:20.000000000 
+0200
+++ new/python-evdev-1.8.0/evdev/genecodes.py   1970-01-01 01:00:00.000000000 
+0100
@@ -1,96 +0,0 @@
-"""
-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:]
-
-
-# -----------------------------------------------------------------------------
-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)
-
-uname = list(os.uname())
-del uname[1]
-uname = " ".join(uname)
-
-
-# -----------------------------------------------------------------------------
-template = r"""
-#include <Python.h>
-#ifdef __FreeBSD__
-#include <dev/evdev/input.h>
-#else
-#include <linux/input.h>
-#include <linux/uinput.h>
-#endif
-
-/* Automatically generated by evdev.genecodes */
-/* Generated on %s */
-
-#define MODULE_NAME "_ecodes"
-#define MODULE_HELP "linux/input.h macros"
-
-static PyMethodDef MethodTable[] = {
-    { NULL, NULL, 0, NULL}
-};
-
-static struct PyModuleDef moduledef = {
-    PyModuleDef_HEAD_INIT,
-    MODULE_NAME,
-    MODULE_HELP,
-    -1,          /* m_size */
-    MethodTable, /* m_methods */
-    NULL,        /* m_reload */
-    NULL,        /* m_traverse */
-    NULL,        /* m_clear */
-    NULL,        /* m_free */
-};
-
-PyMODINIT_FUNC
-PyInit__ecodes(void)
-{
-    PyObject* m = PyModule_Create(&moduledef);
-    if (m == NULL) return NULL;
-
-%s
-
-    return m;
-}
-"""
-
-
-def parse_header(header):
-    for line in open(header):
-        macro = macro_regex.search(line)
-        if macro:
-            yield "    PyModule_AddIntMacro(m, %s);" % macro.group(1)
-
-
-all_macros = []
-for header in headers:
-    try:
-        fh = open(header)
-    except (IOError, OSError):
-        continue
-    all_macros += parse_header(header)
-
-if not all_macros:
-    print("no input macros found in: %s" % " ".join(headers), file=sys.stderr)
-    sys.exit(1)
-
-
-macros = os.linesep.join(all_macros)
-print(template % (uname, macros))
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/python-evdev-1.7.1/evdev/genecodes_c.py 
new/python-evdev-1.8.0/evdev/genecodes_c.py
--- old/python-evdev-1.7.1/evdev/genecodes_c.py 1970-01-01 01:00:00.000000000 
+0100
+++ new/python-evdev-1.8.0/evdev/genecodes_c.py 2025-01-25 17:37:33.000000000 
+0100
@@ -0,0 +1,138 @@
+"""
+Generate a Python extension module with the constants defined in linux/input.h.
+"""
+
+import getopt
+import os
+import re
+import sys
+
+# -----------------------------------------------------------------------------
+# 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",
+]
+
+opts, args = getopt.getopt(sys.argv[1:], "", ["ecodes", "stubs"])
+if not opts:
+    print("usage: genecodes.py [--ecodes|--stubs] <headers>")
+    exit(2)
+
+
+# -----------------------------------------------------------------------------
+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)
+
+# Uname without hostname.
+uname = list(os.uname())
+uname = " ".join((uname[0], *uname[2:]))
+
+
+# -----------------------------------------------------------------------------
+template_ecodes = r"""
+#include <Python.h>
+#ifdef __FreeBSD__
+#include <dev/evdev/input.h>
+#else
+#include <linux/input.h>
+#include <linux/uinput.h>
+#endif
+
+/* Automatically generated by evdev.genecodes */
+/* Generated on   %s */
+/* Generated from %s */
+
+#define MODULE_NAME "_ecodes"
+#define MODULE_HELP "linux/input.h macros"
+
+static PyMethodDef MethodTable[] = {
+    { NULL, NULL, 0, NULL}
+};
+
+static struct PyModuleDef moduledef = {
+    PyModuleDef_HEAD_INIT,
+    MODULE_NAME,
+    MODULE_HELP,
+    -1,          /* m_size */
+    MethodTable, /* m_methods */
+    NULL,        /* m_reload */
+    NULL,        /* m_traverse */
+    NULL,        /* m_clear */
+    NULL,        /* m_free */
+};
+
+PyMODINIT_FUNC
+PyInit__ecodes(void)
+{
+    PyObject* m = PyModule_Create(&moduledef);
+    if (m == NULL) return NULL;
+
+%s
+
+    return m;
+}
+"""
+
+
+template_stubs = r"""
+# Automatically generated by evdev.genecodes
+# Generated on %s
+# Generated from %s
+
+# pylint: skip-file
+
+ecodes: dict[str, int]
+keys: dict[int, str|list[str]]
+bytype: dict[int, dict[int, str|list[str]]]
+
+KEY: dict[int, str|list[str]]
+ABS: dict[int, str|list[str]]
+REL: dict[int, str|list[str]]
+SW:  dict[int, str|list[str]]
+MSC: dict[int, str|list[str]]
+LED: dict[int, str|list[str]]
+BTN: dict[int, str|list[str]]
+REP: dict[int, str|list[str]]
+SND: dict[int, str|list[str]]
+ID:  dict[int, str|list[str]]
+EV:  dict[int, str|list[str]]
+BUS: dict[int, str|list[str]]
+SYN: dict[int, str|list[str]]
+FF_STATUS:     dict[int, str|list[str]]
+FF_INPUT_PROP: dict[int, str|list[str]]
+
+%s
+"""
+
+
+def parse_headers(headers=headers):
+    for header in headers:
+        try:
+            fh = open(header)
+        except (IOError, OSError):
+            continue
+
+        for line in fh:
+            macro = macro_regex.search(line)
+            if macro:
+                yield macro.group(1)
+
+
+all_macros = list(parse_headers())
+if not all_macros:
+    print("no input macros found in: %s" % " ".join(headers), file=sys.stderr)
+    sys.exit(1)
+
+# pylint: disable=possibly-used-before-assignment, used-before-assignment
+if ("--ecodes", "") in opts:
+    body = ("    PyModule_AddIntMacro(m, %s);" % macro for macro in all_macros)
+    template = template_ecodes
+elif ("--stubs", "") in opts:
+    body = ("%s: int" % macro for macro in all_macros)
+    template = template_stubs
+
+body = os.linesep.join(body)
+text = template % (uname, headers, body)
+print(text.strip())
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/python-evdev-1.7.1/evdev/genecodes_py.py 
new/python-evdev-1.8.0/evdev/genecodes_py.py
--- old/python-evdev-1.7.1/evdev/genecodes_py.py        1970-01-01 
01:00:00.000000000 +0100
+++ new/python-evdev-1.8.0/evdev/genecodes_py.py        2025-01-25 
17:37:33.000000000 +0100
@@ -0,0 +1,53 @@
+import sys
+from unittest import mock
+from pprint import PrettyPrinter
+
+sys.modules["evdev.ecodes"] = mock.Mock()
+from evdev import ecodes_runtime as ecodes
+
+pprint = PrettyPrinter(indent=2, sort_dicts=True, width=120).pprint
+
+
+print("# Automatically generated by evdev.genecodes_py")
+print()
+print('"""')
+print(ecodes.__doc__.strip())
+print('"""')
+
+print()
+print("from typing import Final, Dict, Tuple, Union")
+print()
+
+for name, value in ecodes.ecodes.items():
+    print(f"{name}: Final[int] = {value}")
+print()
+
+entries = [
+    ("ecodes", "Dict[str, int]", "#: Mapping of names to values."),
+    ("bytype", "Dict[int, Dict[int, Union[str, Tuple[str]]]]", "#: Mapping of 
event types to other value/name mappings."),
+    ("keys",   "Dict[int, Union[str, Tuple[str]]]", "#: Keys are a combination 
of all BTN and KEY codes."),
+    ("KEY",    "Dict[int, Union[str, Tuple[str]]]", None),
+    ("ABS",    "Dict[int, Union[str, Tuple[str]]]", None),
+    ("REL",    "Dict[int, Union[str, Tuple[str]]]", None),
+    ("SW",     "Dict[int, Union[str, Tuple[str]]]", None),
+    ("MSC",    "Dict[int, Union[str, Tuple[str]]]", None),
+    ("LED",    "Dict[int, Union[str, Tuple[str]]]", None),
+    ("BTN",    "Dict[int, Union[str, Tuple[str]]]", None),
+    ("REP",    "Dict[int, Union[str, Tuple[str]]]", None),
+    ("SND",    "Dict[int, Union[str, Tuple[str]]]", None),
+    ("ID",     "Dict[int, Union[str, Tuple[str]]]", None),
+    ("EV",     "Dict[int, Union[str, Tuple[str]]]", None),
+    ("BUS",    "Dict[int, Union[str, Tuple[str]]]", None),
+    ("SYN",    "Dict[int, Union[str, Tuple[str]]]", None),
+    ("FF",     "Dict[int, Union[str, Tuple[str]]]", None),
+    ("FF_STATUS",  "Dict[int, Union[str, Tuple[str]]]", None),
+    ("INPUT_PROP", "Dict[int, Union[str, Tuple[str]]]", None)
+]
+
+for key, annotation, doc in entries:
+    if doc:
+        print(doc)
+
+    print(f"{key}: {annotation} = ", end="")
+    pprint(getattr(ecodes, key))
+    print()
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/python-evdev-1.7.1/evdev/uinput.py 
new/python-evdev-1.8.0/evdev/uinput.py
--- old/python-evdev-1.7.1/evdev/uinput.py      2024-05-08 01:01:20.000000000 
+0200
+++ new/python-evdev-1.8.0/evdev/uinput.py      2025-01-25 17:37:33.000000000 
+0100
@@ -1,3 +1,4 @@
+import ctypes
 import os
 import platform
 import re
@@ -5,11 +6,8 @@
 import time
 from collections import defaultdict
 
-from evdev import _uinput
-from evdev import ecodes, util, device
-from evdev.events import InputEvent
-import evdev.ff as ff
-import ctypes
+from . import _uinput, device, ecodes, ff, util
+from .events import InputEvent
 
 try:
     from evdev.eventio_async import EventIO
@@ -227,15 +225,6 @@
             _uinput.close(self.fd)
             self.fd = -1
 
-    def syn(self):
-        """
-        Inject a ``SYN_REPORT`` event into the input subsystem. Events
-        queued by :func:`write()` will be fired. If possible, events
-        will be merged into an 'atomic' event.
-        """
-
-        _uinput.write(self.fd, ecodes.EV_SYN, ecodes.SYN_REPORT, 0)
-
     def capabilities(self, verbose=False, absinfo=True):
         """See :func:`capabilities <evdev.device.InputDevice.capabilities>`."""
         if self.device is None:
@@ -277,13 +266,11 @@
         Verify that an uinput device exists and is readable and writable
         by the current process.
         """
-
         try:
             m = os.stat(self.devnode)[stat.ST_MODE]
-            if not stat.S_ISCHR(m):
-                raise
-        except (IndexError, OSError):
-            msg = '"{}" does not exist or is not a character device file ' "- 
verify that the uinput module is loaded"
+            assert stat.S_ISCHR(m)
+        except (IndexError, OSError, AssertionError):
+            msg = '"{}" does not exist or is not a character device file - 
verify that the uinput module is loaded'
             raise UInputError(msg.format(self.devnode))
 
         if not os.access(self.devnode, os.W_OK):
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/python-evdev-1.7.1/evdev/util.py 
new/python-evdev-1.8.0/evdev/util.py
--- old/python-evdev-1.7.1/evdev/util.py        2024-05-08 01:01:20.000000000 
+0200
+++ new/python-evdev-1.8.0/evdev/util.py        2025-01-25 17:37:33.000000000 
+0100
@@ -1,11 +1,11 @@
-import re
+import collections
+import glob
 import os
+import re
 import stat
-import glob
-import collections
 
-from evdev import ecodes
-from evdev.events import event_factory
+from . import ecodes
+from .events import event_factory
 
 
 def list_devices(input_device_dir="/dev/input"):
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/python-evdev-1.7.1/pyproject.toml 
new/python-evdev-1.8.0/pyproject.toml
--- old/python-evdev-1.7.1/pyproject.toml       2024-05-08 01:01:20.000000000 
+0200
+++ new/python-evdev-1.8.0/pyproject.toml       2025-01-25 17:37:33.000000000 
+0100
@@ -4,12 +4,12 @@
 
 [project]
 name = "evdev"
-version = "1.7.1"
+version = "1.8.0"
 description = "Bindings to the Linux input handling subsystem"
 keywords = ["evdev", "input", "uinput"]
 readme = "README.md"
 license = {file = "LICENSE"}
-requires-python = ">=3.6"
+requires-python = ">=3.8"
 authors = [
   { name="Georgi Valkov", email="georgi.t.val...@gmail.com" },
 ]
@@ -39,7 +39,7 @@
 ignore = ["E265", "E241", "F403", "F401", "E401", "E731"]
 
 [tool.bumpversion]
-current_version = "1.7.1"
+current_version = "1.8.0"
 commit = true
 tag = true
 allow_dirty = true
@@ -49,3 +49,12 @@
 
 [[tool.bumpversion.files]]
 filename = "docs/conf.py"
+
+[tool.pylint.'MESSAGES CONTROL']
+disable = """
+    no-member,
+"""
+
+[tool.pylint.typecheck]
+generated-members = ["evdev.ecodes.*"]
+ignored-modules= ["evdev._*"]
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/python-evdev-1.7.1/requirements-dev.txt 
new/python-evdev-1.8.0/requirements-dev.txt
--- old/python-evdev-1.7.1/requirements-dev.txt 2024-05-08 01:01:20.000000000 
+0200
+++ new/python-evdev-1.8.0/requirements-dev.txt 2025-01-25 17:37:33.000000000 
+0100
@@ -6,3 +6,4 @@
 bump-my-version ~= 0.17.4
 build
 twine
+cibuildwheel
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/python-evdev-1.7.1/scripts/build-binary.sh 
new/python-evdev-1.8.0/scripts/build-binary.sh
--- old/python-evdev-1.7.1/scripts/build-binary.sh      1970-01-01 
01:00:00.000000000 +0100
+++ new/python-evdev-1.8.0/scripts/build-binary.sh      2025-01-25 
17:37:33.000000000 +0100
@@ -0,0 +1,15 @@
+#!/usr/bin/env bash
+
+set -o allexport
+set -o nounset
+
+CIBW_MANYLINUX_X86_64_IMAGE="manylinux_2_28"
+CIBW_MANYLINUX_I686_IMAGE="manylinux_2_28"
+CIBW_CONTAINER_ENGINE="podman"
+CIBW_SKIP="cp36-*"
+CIBW_ARCHS_LINUX="auto64"
+CIBW_BEFORE_ALL_LINUX=./scripts/cibw-before.sh
+CIBW_TEST_COMMAND="python -c 'import evdev; print(evdev)'"
+CIBW_ENVIRONMENT="PACKAGE_NAME=evdev-binary"
+
+exec cibuildwheel 
\ No newline at end of file
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/python-evdev-1.7.1/scripts/cibw-before.sh 
new/python-evdev-1.8.0/scripts/cibw-before.sh
--- old/python-evdev-1.7.1/scripts/cibw-before.sh       1970-01-01 
01:00:00.000000000 +0100
+++ new/python-evdev-1.8.0/scripts/cibw-before.sh       2025-01-25 
17:37:33.000000000 +0100
@@ -0,0 +1,6 @@
+#!/usr/bin/env bash
+
+
+if [ -n "$PACKAGE_NAME" ]; then
+    sed -i -re 's,^(name = ")evdev("),\1'${PACKAGE_NAME}'\2,' pyproject.toml
+fi
\ No newline at end of file
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/python-evdev-1.7.1/setup.py 
new/python-evdev-1.8.0/setup.py
--- old/python-evdev-1.7.1/setup.py     2024-05-08 01:01:20.000000000 +0200
+++ new/python-evdev-1.8.0/setup.py     2025-01-25 17:37:33.000000000 +0100
@@ -1,14 +1,16 @@
 import os
 import sys
+import shutil
 import textwrap
 from pathlib import Path
+from subprocess import run
 
 from setuptools import setup, Extension, Command
 from setuptools.command import build_ext as _build_ext
 
 
 curdir = Path(__file__).resolve().parent
-ecodes_path = curdir / "evdev/ecodes.c"
+ecodes_c_path = curdir / "evdev/ecodes.c"
 
 
 def create_ecodes(headers=None):
@@ -47,16 +49,18 @@
               build_ecodes --evdev-headers 
path/input.h:path/input-event-codes.h \\
               build_ext --include-dirs path/ \\
               install
+
+        If you want to avoid building this package from source, then please 
consider
+        installing the `evdev-binary` package instead. Keep in mind that it 
may not be
+        fully compatible with, or support all the features of your current 
kernel.
         """
 
         sys.stderr.write(textwrap.dedent(msg))
         sys.exit(1)
 
-    from subprocess import run
-
-    print("writing %s (using %s)" % (ecodes_path, " ".join(headers)))
-    with ecodes_path.open("w") as fh:
-        cmd = [sys.executable, "evdev/genecodes.py", *headers]
+    print("writing %s (using %s)" % (ecodes_c_path, " ".join(headers)))
+    with ecodes_c_path.open("w") as fh:
+        cmd = [sys.executable, "evdev/genecodes_c.py", "--ecodes", *headers]
         run(cmd, check=True, stdout=fh)
 
 
@@ -80,14 +84,27 @@
 
 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 False
+        return True
+
+    def generate_ecodes_py(self):
+        ecodes_py = Path(self.build_lib) / "evdev/ecodes.py"
+        print(f"writing {ecodes_py}")
+        with ecodes_py.open("w") as fh:
+            cmd = [sys.executable, "-B", "evdev/genecodes_py.py"]
+            res = run(cmd, env={"PYTHONPATH": self.build_lib}, stdout=fh)
+
+        if res.returncode != 0:
+            print(f"failed to generate static {ecodes_py} - will use 
ecodes_runtime.py")
+            shutil.copy("evdev/ecodes_runtime.py", ecodes_py)
 
     def run(self):
         for cmd_name in self.get_sub_commands():
             self.run_command(cmd_name)
         _build_ext.build_ext.run(self)
+        self.generate_ecodes_py()
 
     sub_commands = [("build_ecodes", has_ecodes)] + 
_build_ext.build_ext.sub_commands
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/python-evdev-1.7.1/tests/test_uinput.py 
new/python-evdev-1.8.0/tests/test_uinput.py
--- old/python-evdev-1.7.1/tests/test_uinput.py 2024-05-08 01:01:20.000000000 
+0200
+++ new/python-evdev-1.8.0/tests/test_uinput.py 2025-01-25 17:37:33.000000000 
+0100
@@ -1,10 +1,13 @@
 # encoding: utf-8
-
+import os
+import stat
 from select import select
-from pytest import raises, fixture
+from unittest.mock import patch
 
-from evdev import uinput, ecodes, events, device, util
+import pytest
+from pytest import raises, fixture
 
+from evdev import uinput, ecodes, device, UInputError
 
 # -----------------------------------------------------------------------------
 uinput_options = {
@@ -66,12 +69,12 @@
 
 def test_abs_values(c):
     e = ecodes
-    c["events"] = {
+    c = {
         e.EV_KEY: [e.KEY_A, e.KEY_B],
-        e.EV_ABS: [(e.ABS_X, (0, 255, 0, 0)), (e.ABS_Y, device.AbsInfo(0, 255, 
5, 10, 0, 0))],
+        e.EV_ABS: [(e.ABS_X, (0, 0, 255, 0, 0)), (e.ABS_Y, device.AbsInfo(0, 
0, 255, 5, 10, 0))],
     }
 
-    with uinput.UInput(**c) as ui:
+    with uinput.UInput(events=c) as ui:
         c = ui.capabilities()
         abs = device.AbsInfo(value=0, min=0, max=255, fuzz=0, flat=0, 
resolution=0)
         assert c[e.EV_ABS][0] == (0, abs)
@@ -114,3 +117,21 @@
                 assert evs[3].code == ecodes.KEY_A and evs[3].value == 2
                 assert evs[4].code == ecodes.KEY_A and evs[4].value == 0
                 break
+
+
+@patch.object(stat, 'S_ISCHR', return_value=False)
+def test_not_a_character_device(ischr_mock, c):
+    with pytest.raises(UInputError, match='not a character device file'):
+        uinput.UInput(**c)
+
+@patch.object(stat, 'S_ISCHR', return_value=True)
+@patch.object(os, 'stat', side_effect=OSError())
+def test_not_a_character_device_2(stat_mock, ischr_mock, c):
+    with pytest.raises(UInputError, match='not a character device file'):
+        uinput.UInput(**c)
+
+@patch.object(stat, 'S_ISCHR', return_value=True)
+@patch.object(os, 'stat', return_value=[])
+def test_not_a_character_device_3(stat_mock, ischr_mock, c):
+    with pytest.raises(UInputError, match='not a character device file'):
+        uinput.UInput(**c)

Reply via email to