udev-cli 0.1.0__tar.gz

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.
@@ -0,0 +1,55 @@
1
+ Metadata-Version: 2.4
2
+ Name: udev-cli
3
+ Version: 0.1.0
4
+ Summary: Print, filter, and react to udev events as JSON
5
+ Author: readwithai
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/readwithai/udev-cli
8
+ Classifier: Development Status :: 3 - Alpha
9
+ Classifier: Environment :: Console
10
+ Classifier: Operating System :: POSIX :: Linux
11
+ Classifier: Programming Language :: Python :: 3
12
+ Classifier: Topic :: System :: Hardware
13
+ Requires-Python: >=3.9
14
+ Description-Content-Type: text/markdown
15
+ Requires-Dist: pyudev>=0.22
16
+
17
+ # udev-cli
18
+ Print udev events, filter udev events, and run commands on udev events. udev is a system of events when hardware devices are plugged into a computer on linux.
19
+
20
+ This is unreviewed AI-generated code. But will become more reviewed if people use it.
21
+
22
+ ## Motivation
23
+ I have a tried to use udev a few times. But I always find it difficult to actually get my events to fire and debug what is going on. I've decied to just listen to events myself and trigger events rather than mess ith rules.
24
+
25
+
26
+ ## Alternatives and prior work
27
+ `udev-monitor` is a pypi project which is very similar. However, it could not filter by UUID, so I decided to vibe code something up.
28
+
29
+ `udev` itself has a rule system and
30
+
31
+ `udevadm monitor` can output events from udev put does not output JSON. I am also concerned about buffering.
32
+
33
+ This uses pyudev internally. You could achieve this functionality with a little python script using `pyudev` and do other things - but it would involve a little more debugging to understand what is going on.
34
+
35
+
36
+ ## Installation
37
+ pipx install udev-cli
38
+
39
+ ## Usage
40
+ Print all udev events: `udev-cli`. You can use `jq` to help you work out what you want to match.
41
+
42
+ block device events only: `udev-cli -s block`
43
+
44
+ block devices added: `udev-cli -s block -a add`
45
+
46
+ match specific UUID: `udev-cli -f ID_FS_UUID b9335c96-...`
47
+
48
+ match any UUID in file: `udev-cli -f ID_FS_UUID @/etc/backup-uuids.txt`
49
+
50
+ run a command: `udev-cli -s block -a add -f ID_FS_UUID @uuids --run ./backup.sh`
51
+
52
+ ## About
53
+ I am @readwithai. I am making tools for reading and agency with and without AI.
54
+ I also make a lot of small tools related to productivity. If this tool is useful you might like [follow me on github](https://github.com/talwrii) or [X](https://x.com/readwithai).
55
+
@@ -0,0 +1,39 @@
1
+ # udev-cli
2
+ Print udev events, filter udev events, and run commands on udev events. udev is a system of events when hardware devices are plugged into a computer on linux.
3
+
4
+ This is unreviewed AI-generated code. But will become more reviewed if people use it.
5
+
6
+ ## Motivation
7
+ I have a tried to use udev a few times. But I always find it difficult to actually get my events to fire and debug what is going on. I've decied to just listen to events myself and trigger events rather than mess ith rules.
8
+
9
+
10
+ ## Alternatives and prior work
11
+ `udev-monitor` is a pypi project which is very similar. However, it could not filter by UUID, so I decided to vibe code something up.
12
+
13
+ `udev` itself has a rule system and
14
+
15
+ `udevadm monitor` can output events from udev put does not output JSON. I am also concerned about buffering.
16
+
17
+ This uses pyudev internally. You could achieve this functionality with a little python script using `pyudev` and do other things - but it would involve a little more debugging to understand what is going on.
18
+
19
+
20
+ ## Installation
21
+ pipx install udev-cli
22
+
23
+ ## Usage
24
+ Print all udev events: `udev-cli`. You can use `jq` to help you work out what you want to match.
25
+
26
+ block device events only: `udev-cli -s block`
27
+
28
+ block devices added: `udev-cli -s block -a add`
29
+
30
+ match specific UUID: `udev-cli -f ID_FS_UUID b9335c96-...`
31
+
32
+ match any UUID in file: `udev-cli -f ID_FS_UUID @/etc/backup-uuids.txt`
33
+
34
+ run a command: `udev-cli -s block -a add -f ID_FS_UUID @uuids --run ./backup.sh`
35
+
36
+ ## About
37
+ I am @readwithai. I am making tools for reading and agency with and without AI.
38
+ I also make a lot of small tools related to productivity. If this tool is useful you might like [follow me on github](https://github.com/talwrii) or [X](https://x.com/readwithai).
39
+
@@ -0,0 +1,30 @@
1
+ [build-system]
2
+ requires = ["setuptools>=68.0"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "udev-cli"
7
+ version = "0.1.0"
8
+ description = "Print, filter, and react to udev events as JSON"
9
+ readme = "README.md"
10
+ license = "MIT"
11
+ requires-python = ">=3.9"
12
+ authors = [
13
+ { name = "readwithai" },
14
+ ]
15
+ dependencies = [
16
+ "pyudev>=0.22",
17
+ ]
18
+ classifiers = [
19
+ "Development Status :: 3 - Alpha",
20
+ "Environment :: Console",
21
+ "Operating System :: POSIX :: Linux",
22
+ "Programming Language :: Python :: 3",
23
+ "Topic :: System :: Hardware",
24
+ ]
25
+
26
+ [project.scripts]
27
+ udev-cli = "udev_cli.main:main"
28
+
29
+ [project.urls]
30
+ Homepage = "https://github.com/readwithai/udev-cli"
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
File without changes
@@ -0,0 +1,134 @@
1
+ """udev-cli: Print, filter, and react to udev events."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import argparse
6
+ import json
7
+ import subprocess
8
+ import sys
9
+ from typing import Optional
10
+
11
+
12
+ def device_to_dict(device, action: Optional[str] = None) -> dict:
13
+ """Convert a pyudev Device to a JSON-serialisable dict."""
14
+ d = {
15
+ "action": action or device.action,
16
+ "sys_path": device.sys_path,
17
+ "sys_name": device.sys_name,
18
+ "device_node": device.device_node,
19
+ "device_type": device.device_type,
20
+ "subsystem": device.subsystem,
21
+ "driver": device.driver,
22
+ "device_path": device.device_path,
23
+ "tags": list(device.tags),
24
+ "properties": dict(device.properties),
25
+ }
26
+ return d
27
+
28
+
29
+ def load_values_file(path: str) -> list[str]:
30
+ """Load values from a file, one per line."""
31
+ values = []
32
+ with open(path) as f:
33
+ for line in f:
34
+ line = line.strip()
35
+ if not line or line.startswith("#"):
36
+ continue
37
+ values.append(line)
38
+ return values
39
+
40
+
41
+ def resolve_filters(raw_filters: list[list[str]]) -> list[tuple[str, list[str]]]:
42
+ """Resolve -f args into (key, [values]) pairs.
43
+
44
+ -f KEY VALUE -> (KEY, [VALUE])
45
+ -f KEY @file -> (KEY, [val1, val2, ...])
46
+
47
+ All pairs must match (AND). Multiple values = any can match (OR).
48
+ """
49
+ resolved = []
50
+ for parts in raw_filters:
51
+ if len(parts) != 2:
52
+ print(f"Error: -f expects KEY VALUE, got: {' '.join(parts)}", file=sys.stderr)
53
+ sys.exit(1)
54
+ key, value = parts
55
+ if value.startswith("@"):
56
+ path = value[1:]
57
+ values = load_values_file(path)
58
+ if not values:
59
+ print(f"Warning: no values in {path}", file=sys.stderr)
60
+ resolved.append((key, values))
61
+ else:
62
+ resolved.append((key, [value]))
63
+ return resolved
64
+
65
+
66
+ def matches_filters(device, action: Optional[str], filters: list[tuple[str, list[str]]], actions: list[str]) -> bool:
67
+ """Check if a device event matches filters.
68
+
69
+ All filter keys must match (AND).
70
+ For each key, device value must be in the values list (OR).
71
+ """
72
+ act = action or device.action
73
+ if actions and act not in actions:
74
+ return False
75
+
76
+ for key, values in filters:
77
+ dev_value = device.get(key)
78
+ if dev_value is None or dev_value not in values:
79
+ return False
80
+
81
+ return True
82
+
83
+
84
+ def cmd_monitor(args):
85
+ """Monitor udev events and print as JSONL."""
86
+ import pyudev
87
+
88
+ context = pyudev.Context()
89
+ monitor = pyudev.Monitor.from_netlink(context)
90
+
91
+ if args.subsystem:
92
+ for sub in args.subsystem:
93
+ monitor.filter_by(sub)
94
+
95
+ filters = resolve_filters(args.filter or [])
96
+ actions = [a.strip() for a in args.action.split(",")] if args.action else []
97
+
98
+ for device in iter(monitor.poll, None):
99
+ action = device.action
100
+
101
+ if not matches_filters(device, action, filters, actions):
102
+ continue
103
+
104
+ event = device_to_dict(device, action)
105
+
106
+ line = json.dumps(event)
107
+ print(line, flush=True)
108
+
109
+ if args.run:
110
+ try:
111
+ subprocess.Popen(
112
+ [args.run, line],
113
+ stdout=None, stderr=None,
114
+ )
115
+ except Exception as e:
116
+ print(json.dumps({"error": str(e)}), file=sys.stderr, flush=True)
117
+
118
+
119
+ def main():
120
+ parser = argparse.ArgumentParser(
121
+ description="Print, filter, and react to udev events as JSONL",
122
+ prog="udev-cli",
123
+ )
124
+ parser.add_argument("-s", "--subsystem", action="append", help="Filter by subsystem (e.g. block, usb, input)")
125
+ parser.add_argument("-a", "--action", help="Filter by action (comma-separated: add,remove,change)")
126
+ parser.add_argument("-f", "--filter", nargs=2, action="append", metavar=("KEY", "VALUE"), help="Filter by property: KEY VALUE or KEY @filepath")
127
+ parser.add_argument("--run", help="Run command on match (event JSON passed as first arg)")
128
+
129
+ args = parser.parse_args()
130
+ cmd_monitor(args)
131
+
132
+
133
+ if __name__ == "__main__":
134
+ main()
@@ -0,0 +1,55 @@
1
+ Metadata-Version: 2.4
2
+ Name: udev-cli
3
+ Version: 0.1.0
4
+ Summary: Print, filter, and react to udev events as JSON
5
+ Author: readwithai
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/readwithai/udev-cli
8
+ Classifier: Development Status :: 3 - Alpha
9
+ Classifier: Environment :: Console
10
+ Classifier: Operating System :: POSIX :: Linux
11
+ Classifier: Programming Language :: Python :: 3
12
+ Classifier: Topic :: System :: Hardware
13
+ Requires-Python: >=3.9
14
+ Description-Content-Type: text/markdown
15
+ Requires-Dist: pyudev>=0.22
16
+
17
+ # udev-cli
18
+ Print udev events, filter udev events, and run commands on udev events. udev is a system of events when hardware devices are plugged into a computer on linux.
19
+
20
+ This is unreviewed AI-generated code. But will become more reviewed if people use it.
21
+
22
+ ## Motivation
23
+ I have a tried to use udev a few times. But I always find it difficult to actually get my events to fire and debug what is going on. I've decied to just listen to events myself and trigger events rather than mess ith rules.
24
+
25
+
26
+ ## Alternatives and prior work
27
+ `udev-monitor` is a pypi project which is very similar. However, it could not filter by UUID, so I decided to vibe code something up.
28
+
29
+ `udev` itself has a rule system and
30
+
31
+ `udevadm monitor` can output events from udev put does not output JSON. I am also concerned about buffering.
32
+
33
+ This uses pyudev internally. You could achieve this functionality with a little python script using `pyudev` and do other things - but it would involve a little more debugging to understand what is going on.
34
+
35
+
36
+ ## Installation
37
+ pipx install udev-cli
38
+
39
+ ## Usage
40
+ Print all udev events: `udev-cli`. You can use `jq` to help you work out what you want to match.
41
+
42
+ block device events only: `udev-cli -s block`
43
+
44
+ block devices added: `udev-cli -s block -a add`
45
+
46
+ match specific UUID: `udev-cli -f ID_FS_UUID b9335c96-...`
47
+
48
+ match any UUID in file: `udev-cli -f ID_FS_UUID @/etc/backup-uuids.txt`
49
+
50
+ run a command: `udev-cli -s block -a add -f ID_FS_UUID @uuids --run ./backup.sh`
51
+
52
+ ## About
53
+ I am @readwithai. I am making tools for reading and agency with and without AI.
54
+ I also make a lot of small tools related to productivity. If this tool is useful you might like [follow me on github](https://github.com/talwrii) or [X](https://x.com/readwithai).
55
+
@@ -0,0 +1,10 @@
1
+ README.md
2
+ pyproject.toml
3
+ udev_cli/__init__.py
4
+ udev_cli/main.py
5
+ udev_cli.egg-info/PKG-INFO
6
+ udev_cli.egg-info/SOURCES.txt
7
+ udev_cli.egg-info/dependency_links.txt
8
+ udev_cli.egg-info/entry_points.txt
9
+ udev_cli.egg-info/requires.txt
10
+ udev_cli.egg-info/top_level.txt
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ udev-cli = udev_cli.main:main
@@ -0,0 +1 @@
1
+ pyudev>=0.22
@@ -0,0 +1 @@
1
+ udev_cli