plexus-python 0.1.0__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.
Files changed (50) hide show
  1. plexus/__init__.py +31 -0
  2. plexus/__main__.py +4 -0
  3. plexus/adapters/__init__.py +122 -0
  4. plexus/adapters/base.py +409 -0
  5. plexus/adapters/ble.py +257 -0
  6. plexus/adapters/can.py +439 -0
  7. plexus/adapters/can_detect.py +174 -0
  8. plexus/adapters/mavlink.py +642 -0
  9. plexus/adapters/mavlink_detect.py +192 -0
  10. plexus/adapters/modbus.py +622 -0
  11. plexus/adapters/mqtt.py +350 -0
  12. plexus/adapters/opcua.py +607 -0
  13. plexus/adapters/registry.py +206 -0
  14. plexus/adapters/serial_adapter.py +547 -0
  15. plexus/buffer.py +257 -0
  16. plexus/cameras/__init__.py +57 -0
  17. plexus/cameras/auto.py +239 -0
  18. plexus/cameras/base.py +189 -0
  19. plexus/cameras/picamera.py +171 -0
  20. plexus/cameras/usb.py +143 -0
  21. plexus/cli.py +783 -0
  22. plexus/client.py +465 -0
  23. plexus/config.py +169 -0
  24. plexus/connector.py +666 -0
  25. plexus/deps.py +246 -0
  26. plexus/detect.py +1238 -0
  27. plexus/importers/__init__.py +25 -0
  28. plexus/importers/rosbag.py +778 -0
  29. plexus/sensors/__init__.py +118 -0
  30. plexus/sensors/ads1115.py +164 -0
  31. plexus/sensors/adxl345.py +179 -0
  32. plexus/sensors/auto.py +290 -0
  33. plexus/sensors/base.py +412 -0
  34. plexus/sensors/bh1750.py +102 -0
  35. plexus/sensors/bme280.py +241 -0
  36. plexus/sensors/gps.py +317 -0
  37. plexus/sensors/ina219.py +149 -0
  38. plexus/sensors/magnetometer.py +239 -0
  39. plexus/sensors/mpu6050.py +162 -0
  40. plexus/sensors/sht3x.py +139 -0
  41. plexus/sensors/spi_scan.py +164 -0
  42. plexus/sensors/system.py +261 -0
  43. plexus/sensors/vl53l0x.py +109 -0
  44. plexus/streaming.py +743 -0
  45. plexus/tui.py +642 -0
  46. plexus_python-0.1.0.dist-info/METADATA +470 -0
  47. plexus_python-0.1.0.dist-info/RECORD +50 -0
  48. plexus_python-0.1.0.dist-info/WHEEL +4 -0
  49. plexus_python-0.1.0.dist-info/entry_points.txt +2 -0
  50. plexus_python-0.1.0.dist-info/licenses/LICENSE +190 -0
plexus/deps.py ADDED
@@ -0,0 +1,246 @@
1
+ """
2
+ Lazy dependency management for Plexus optional extras.
3
+
4
+ Provides helpful error messages when optional packages are missing,
5
+ and auto-install support for CLI usage.
6
+
7
+ Usage in adapter/sensor code:
8
+ from plexus.deps import require
9
+
10
+ # In a function that needs an optional dependency:
11
+ smbus2 = require("smbus2", extra="sensors")
12
+ bus = smbus2.SMBus(1)
13
+
14
+ # Or check availability without raising:
15
+ if is_available("opencv-python"):
16
+ import cv2
17
+
18
+ CLI auto-install:
19
+ When running via CLI with --auto-install, missing dependencies
20
+ are installed automatically instead of raising errors.
21
+ """
22
+
23
+ import importlib
24
+ import logging
25
+ import subprocess
26
+ import sys
27
+ from typing import Optional
28
+
29
+ logger = logging.getLogger(__name__)
30
+
31
+ # Maps import names to pip package names and the plexus extra that includes them
32
+ DEPENDENCY_MAP = {
33
+ # import_name: (pip_package, plexus_extra, description)
34
+ "smbus2": ("smbus2>=0.4.0", "sensors", "I2C sensor communication"),
35
+ "cv2": ("opencv-python>=4.8.0", "camera", "USB webcam support"),
36
+ "numpy": ("numpy>=1.20.0", "camera", "Numerical arrays for camera frames"),
37
+ "picamera2": ("picamera2>=0.3.12", "picamera", "Raspberry Pi Camera Module"),
38
+ "paho": ("paho-mqtt>=1.6.0", "mqtt", "MQTT broker bridging"),
39
+ "can": ("python-can>=4.0.0", "can", "CAN bus interface"),
40
+ "cantools": ("cantools>=39.0.0", "can", "DBC file parsing"),
41
+ "rosbags": ("rosbags>=0.9.0", "ros", "ROS bag file reading"),
42
+ "mcap": ("mcap>=1.0.0", "ros", "MCAP format support"),
43
+ "serial": ("pyserial>=3.5", "serial", "Serial port communication"),
44
+ "rich": ("rich>=13.0.0", "tui", "Rich terminal output"),
45
+ "bluetooth": ("pybluez>=0.23", "bluetooth", "Bluetooth device scanning"),
46
+ "psutil": ("psutil>=5.9.0", "system", "System resource monitoring"),
47
+ }
48
+
49
+ # Global flag for CLI auto-install mode
50
+ _auto_install_enabled = False
51
+
52
+
53
+ def enable_auto_install():
54
+ """Enable automatic installation of missing dependencies (CLI mode)."""
55
+ global _auto_install_enabled
56
+ _auto_install_enabled = True
57
+
58
+
59
+ def disable_auto_install():
60
+ """Disable automatic installation."""
61
+ global _auto_install_enabled
62
+ _auto_install_enabled = False
63
+
64
+
65
+ def is_available(import_name: str) -> bool:
66
+ """Check if a package is importable without raising."""
67
+ try:
68
+ importlib.import_module(import_name)
69
+ return True
70
+ except ImportError:
71
+ return False
72
+
73
+
74
+ def require(import_name: str, extra: Optional[str] = None):
75
+ """
76
+ Import and return a module, with helpful errors and optional auto-install.
77
+
78
+ Args:
79
+ import_name: The Python import name (e.g., "smbus2", "cv2")
80
+ extra: The plexus extra that provides this (e.g., "sensors")
81
+
82
+ Returns:
83
+ The imported module
84
+
85
+ Raises:
86
+ ImportError: With a helpful message including the pip install command
87
+ """
88
+ try:
89
+ return importlib.import_module(import_name)
90
+ except ImportError:
91
+ pass
92
+
93
+ # Look up package info
94
+ dep_info = DEPENDENCY_MAP.get(import_name)
95
+ if dep_info:
96
+ pip_package, plexus_extra, description = dep_info
97
+ else:
98
+ pip_package = import_name
99
+ plexus_extra = extra
100
+ description = import_name
101
+
102
+ # Try auto-install if enabled
103
+ if _auto_install_enabled:
104
+ if _pip_install(pip_package, description):
105
+ return importlib.import_module(import_name)
106
+
107
+ # Build helpful error message
108
+ if plexus_extra:
109
+ msg = (
110
+ f"\n"
111
+ f" Missing dependency: {import_name} ({description})\n"
112
+ f"\n"
113
+ f" Install with:\n"
114
+ f" pip install plexus-python[{plexus_extra}]\n"
115
+ f"\n"
116
+ f" Or install directly:\n"
117
+ f" pip install {pip_package}\n"
118
+ )
119
+ else:
120
+ msg = (
121
+ f"\n"
122
+ f" Missing dependency: {import_name} ({description})\n"
123
+ f"\n"
124
+ f" Install with:\n"
125
+ f" pip install {pip_package}\n"
126
+ )
127
+
128
+ raise ImportError(msg)
129
+
130
+
131
+ def prompt_install(import_name: str, extra: Optional[str] = None) -> bool:
132
+ """
133
+ Check if a dependency is available; if not, prompt user to install.
134
+
135
+ For CLI interactive use. Returns True if the dependency is available
136
+ (either already installed or just installed).
137
+
138
+ Args:
139
+ import_name: The Python import name
140
+ extra: The plexus extra that provides this
141
+
142
+ Returns:
143
+ True if the module is now importable
144
+ """
145
+ if is_available(import_name):
146
+ return True
147
+
148
+ dep_info = DEPENDENCY_MAP.get(import_name)
149
+ if dep_info:
150
+ pip_package, plexus_extra, description = dep_info
151
+ else:
152
+ pip_package = import_name
153
+ plexus_extra = extra
154
+ description = import_name
155
+
156
+ # Auto-install mode: don't prompt
157
+ if _auto_install_enabled:
158
+ return _pip_install(pip_package, description)
159
+
160
+ # Interactive prompt
161
+ try:
162
+ import click
163
+
164
+ click.echo()
165
+ click.secho(f" {description} requires '{import_name}' which is not installed.", fg="yellow")
166
+ click.echo()
167
+
168
+ if plexus_extra:
169
+ click.echo(f" Install with: pip install plexus-python[{plexus_extra}]")
170
+ else:
171
+ click.echo(f" Install with: pip install {pip_package}")
172
+
173
+ click.echo()
174
+
175
+ if click.confirm(" Install now?", default=True):
176
+ return _pip_install(pip_package, description)
177
+ return False
178
+
179
+ except (ImportError, EOFError):
180
+ return False
181
+
182
+
183
+ def _pip_install(package: str, description: str) -> bool:
184
+ """Install a package via pip. Returns True on success."""
185
+ try:
186
+ import click
187
+ click.secho(f" Installing {description}...", fg="cyan", nl=False)
188
+ except ImportError:
189
+ pass
190
+
191
+ try:
192
+ result = subprocess.run(
193
+ [sys.executable, "-m", "pip", "install", "-q", package],
194
+ capture_output=True,
195
+ text=True,
196
+ timeout=120,
197
+ )
198
+
199
+ if result.returncode == 0:
200
+ try:
201
+ import click
202
+ click.secho(" done", fg="green")
203
+ except ImportError:
204
+ pass
205
+ logger.info(f"Installed {package}")
206
+ return True
207
+ else:
208
+ try:
209
+ import click
210
+ click.secho(" failed", fg="red")
211
+ if result.stderr:
212
+ click.secho(f" {result.stderr.strip()[:200]}", fg="bright_black")
213
+ except ImportError:
214
+ pass
215
+ logger.warning(f"Failed to install {package}: {result.stderr}")
216
+ return False
217
+
218
+ except subprocess.TimeoutExpired:
219
+ try:
220
+ import click
221
+ click.secho(" timed out", fg="red")
222
+ except ImportError:
223
+ pass
224
+ return False
225
+ except Exception as e:
226
+ logger.warning(f"pip install failed: {e}")
227
+ return False
228
+
229
+
230
+ def check_extras_for_scan() -> dict:
231
+ """
232
+ Check which optional extras are installed.
233
+ Returns a dict of {extra_name: bool}.
234
+ Used by `plexus doctor` and `plexus scan`.
235
+ """
236
+ extras = {
237
+ "sensors": is_available("smbus2"),
238
+ "camera": is_available("cv2"),
239
+ "picamera": is_available("picamera2"),
240
+ "mqtt": is_available("paho"),
241
+ "can": is_available("can"),
242
+ "ros": is_available("rosbags"),
243
+ "serial": is_available("serial"),
244
+ "tui": is_available("rich"),
245
+ }
246
+ return extras