python-libei 0.6.0__py3-none-any.whl → 0.7.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.
libei/__init__.py CHANGED
@@ -31,6 +31,6 @@ the full breakdown, including which features need which libei version.
31
31
  Beta: the API is not frozen.
32
32
  """
33
33
 
34
- __version__ = "0.6.0"
34
+ __version__ = "0.7.0"
35
35
 
36
36
  __all__ = ["__version__"]
libei/_capi/libei.py CHANGED
@@ -128,7 +128,7 @@ event_scroll_get_discrete_dy = lib.function(
128
128
  event_touch_get_id = lib.function("ei_event_touch_get_id", (c_void_p,), c_uint32)
129
129
  event_touch_get_x = lib.function("ei_event_touch_get_x", (c_void_p,), c_double)
130
130
  event_touch_get_y = lib.function("ei_event_touch_get_y", (c_void_p,), c_double)
131
- event_touch_get_is_cancel = lib.function(
131
+ event_touch_get_is_cancel = lib.function( # libei 1.4+
132
132
  "ei_event_touch_get_is_cancel", (c_void_p,), c_bool
133
133
  )
134
134
  event_text_get_utf8 = lib.function( # libei 1.6+
@@ -141,7 +141,9 @@ event_text_get_keysym_is_press = lib.function( # libei 1.6+
141
141
  "ei_event_text_get_keysym_is_press", (c_void_p,), c_bool
142
142
  )
143
143
  # Borrowed reference -- the event still owns it, so wrap() rather than adopt().
144
- event_pong_get_ping = lib.function("ei_event_pong_get_ping", (c_void_p,), c_void_p)
144
+ event_pong_get_ping = lib.function( # libei 1.4+
145
+ "ei_event_pong_get_ping", (c_void_p,), c_void_p
146
+ )
145
147
 
146
148
  device_ref = lib.function("ei_device_ref", (c_void_p,), c_void_p)
147
149
  device_unref = lib.function("ei_device_unref", (c_void_p,), c_void_p)
@@ -236,7 +238,7 @@ touch_get_device = lib.function("ei_touch_get_device", (c_void_p,), c_void_p)
236
238
  touch_down = lib.function("ei_touch_down", (c_void_p, c_double, c_double), None)
237
239
  touch_motion = lib.function("ei_touch_motion", (c_void_p, c_double, c_double), None)
238
240
  touch_up = lib.function("ei_touch_up", (c_void_p,), None)
239
- touch_cancel = lib.function("ei_touch_cancel", (c_void_p,), None)
241
+ touch_cancel = lib.function("ei_touch_cancel", (c_void_p,), None) # libei 1.4+
240
242
 
241
243
  # ei_new_ping() returns an owned reference; ei_ping() then triggers the round
242
244
  # trip that comes back as an EI_EVENT_PONG.
libei/_capi/libeis.py CHANGED
@@ -104,7 +104,7 @@ event_scroll_get_discrete_dy = lib.function(
104
104
  event_touch_get_id = lib.function("eis_event_touch_get_id", (c_void_p,), c_uint32)
105
105
  event_touch_get_x = lib.function("eis_event_touch_get_x", (c_void_p,), c_double)
106
106
  event_touch_get_y = lib.function("eis_event_touch_get_y", (c_void_p,), c_double)
107
- event_touch_get_is_cancel = lib.function(
107
+ event_touch_get_is_cancel = lib.function( # libei 1.4+
108
108
  "eis_event_touch_get_is_cancel", (c_void_p,), c_bool
109
109
  )
110
110
  event_text_get_utf8 = lib.function( # libei 1.6+
@@ -117,14 +117,16 @@ event_text_get_keysym_is_press = lib.function( # libei 1.6+
117
117
  "eis_event_text_get_keysym_is_press", (c_void_p,), c_bool
118
118
  )
119
119
  # Borrowed reference -- the event still owns it, so wrap() rather than adopt().
120
- event_pong_get_ping = lib.function("eis_event_pong_get_ping", (c_void_p,), c_void_p)
120
+ event_pong_get_ping = lib.function( # libei 1.4+
121
+ "eis_event_pong_get_ping", (c_void_p,), c_void_p
122
+ )
121
123
 
122
124
  client_ref = lib.function("eis_client_ref", (c_void_p,), c_void_p)
123
125
  client_unref = lib.function("eis_client_unref", (c_void_p,), c_void_p)
124
126
  client_is_sender = lib.function("eis_client_is_sender", (c_void_p,), c_bool)
125
127
  # pid_t, i.e. a 32-bit signed int on Linux. Socket backend only, and
126
128
  # negative errno on failure.
127
- backend_socket_get_client_pid = lib.function(
129
+ backend_socket_get_client_pid = lib.function( # libei 1.5+
128
130
  "eis_backend_socket_get_client_pid", (c_void_p,), c_int32
129
131
  )
130
132
  client_get_name = lib.function("eis_client_get_name", (c_void_p,), c_char_p)
@@ -277,7 +279,7 @@ touch_get_device = lib.function("eis_touch_get_device", (c_void_p,), c_void_p)
277
279
  touch_down = lib.function("eis_touch_down", (c_void_p, c_double, c_double), None)
278
280
  touch_motion = lib.function("eis_touch_motion", (c_void_p, c_double, c_double), None)
279
281
  touch_up = lib.function("eis_touch_up", (c_void_p,), None)
280
- touch_cancel = lib.function("eis_touch_cancel", (c_void_p,), None)
282
+ touch_cancel = lib.function("eis_touch_cancel", (c_void_p,), None) # libei 1.4+
281
283
 
282
284
  ping = lib.function("eis_ping", (c_void_p,), None) # libei 1.4+
283
285
  ping_get_id = lib.function("eis_ping_get_id", (c_void_p,), c_uint64) # libei 1.4+
libei/_capi/loader.py CHANGED
@@ -30,6 +30,7 @@ class LazyLibrary:
30
30
  self._lib: ctypes.CDLL | None = None
31
31
  self._load_error: OSError | None = None
32
32
  self._lock = threading.Lock()
33
+ self._declared: dict[str, tuple[tuple[type, ...], type | None]] = {}
33
34
 
34
35
  def _ensure_loaded(self) -> ctypes.CDLL:
35
36
  """Return the opened library, opening it on first call.
@@ -75,6 +76,21 @@ class LazyLibrary:
75
76
  return False
76
77
  return True
77
78
 
79
+ @property
80
+ def soname(self) -> str:
81
+ """The shared library this binds, as it is passed to ``dlopen``."""
82
+ return self._soname
83
+
84
+ @property
85
+ def declared(self) -> dict[str, tuple[tuple[type, ...], type | None]]:
86
+ """Every function bound so far: C name -> (argtypes, restype).
87
+
88
+ Read by the ABI tests, which compare it with the library's exports and
89
+ with the upstream headers. Declaring a binding records it here and does
90
+ nothing else -- the library is still not opened until a call.
91
+ """
92
+ return dict(self._declared)
93
+
78
94
  def function(
79
95
  self,
80
96
  name: str,
@@ -94,6 +110,7 @@ class LazyLibrary:
94
110
  # the dict is chosen only because "absent from the dict" already
95
111
  # means "not resolved yet", with no None sentinel to confuse with a
96
112
  # legitimately-None value.
113
+ self._declared[name] = (tuple(argtypes), restype)
97
114
  cache: dict[str, Any] = {}
98
115
 
99
116
  def call(*args: Any) -> Any:
@@ -113,7 +130,23 @@ class LazyLibrary:
113
130
  bound.argtypes = list(argtypes)
114
131
  bound.restype = restype
115
132
  cache["f"] = bound
116
- return bound(*args)
133
+ try:
134
+ return bound(*args)
135
+ except ctypes.ArgumentError:
136
+ # ctypes turns *any* exception raised while converting an
137
+ # argument into an ArgumentError carrying only its text -- no
138
+ # __cause__, no __context__. For a released object that loses
139
+ # the RuntimeError its ``_as_parameter_`` raised, so a caller
140
+ # catching RuntimeError (as documented) would miss it. Ask the
141
+ # arguments again and re-raise the real one.
142
+ for arg in args:
143
+ try:
144
+ arg._as_parameter_ # noqa: B018 - evaluated for its error
145
+ except RuntimeError as released:
146
+ raise released from None
147
+ except AttributeError:
148
+ continue
149
+ raise
117
150
 
118
151
  call.__name__ = name
119
152
  return call
libei/portal.py CHANGED
@@ -185,6 +185,28 @@ class PortalDeniedError(PortalError):
185
185
  self.message = message
186
186
 
187
187
 
188
+ class PortalSessionClosedError(PortalError):
189
+ """The portal session ended while something was still waiting on it.
190
+
191
+ The ``org.freedesktop.portal.Session`` interface emits ``Closed`` when
192
+ the compositor or the portal ends a session from its side -- a
193
+ compositor restart, the portal process dying, a session revoked from a
194
+ settings panel. Distinct from :class:`PortalTimeoutError` (the session
195
+ is fine, the signal just has not come) and from
196
+ :class:`PortalDeniedError` (a request was answered, negatively): here
197
+ there is no session left to answer anything, so the wait that raised
198
+ this can never be satisfied by waiting longer.
199
+ """
200
+
201
+ def __init__(self, step: str) -> None:
202
+ """Name what was being waited for when the session went away."""
203
+ super().__init__(
204
+ f"the portal session was closed while waiting for {step}; "
205
+ "the compositor or portal ended it from its side"
206
+ )
207
+ self.step = step
208
+
209
+
188
210
  def _gio() -> tuple[Any, Any] | None:
189
211
  """Import Gio and GLib, or return None.
190
212
 
@@ -1038,6 +1060,27 @@ def _wait_for_signal(
1038
1060
  loop = GLib.MainLoop()
1039
1061
  result: dict[str, Any] = {}
1040
1062
  timed_out = False
1063
+ session_closed = False
1064
+
1065
+ def on_closed(*_args: Any) -> None:
1066
+ """End the wait: a closed session can never deliver the signal.
1067
+
1068
+ ``Closed`` is emitted on the **session object's own path** (unlike
1069
+ ``Activated``/``Deactivated``, which fire on the portal object --
1070
+ see above), so the subscription below uses ``session_handle`` as
1071
+ the path and needs no payload filtering: any ``Closed`` there is
1072
+ this session's. Without this subscription, a wait with
1073
+ ``timeout=None`` on a session the compositor has ended -- a
1074
+ compositor restart, the portal dying, capture revoked from a
1075
+ settings panel -- blocked forever, indistinguishable from a signal
1076
+ that merely had not fired yet.
1077
+ """
1078
+ nonlocal session_closed
1079
+ if result:
1080
+ return
1081
+ logger.debug("session %s closed while waiting for %s", session_handle, signal)
1082
+ session_closed = True
1083
+ loop.quit()
1041
1084
 
1042
1085
  def on_signal(
1043
1086
  _conn: Any,
@@ -1086,6 +1129,16 @@ def _wait_for_signal(
1086
1129
  on_signal,
1087
1130
  None,
1088
1131
  )
1132
+ closed_subscription = connection.signal_subscribe(
1133
+ busname,
1134
+ _SESSION_INTERFACE,
1135
+ "Closed",
1136
+ session_handle,
1137
+ None,
1138
+ Gio.DBusSignalFlags.NONE,
1139
+ on_closed,
1140
+ None,
1141
+ )
1089
1142
  logger.debug(
1090
1143
  "waiting up to %s for %s.%s on %s for session %s",
1091
1144
  "forever" if timeout is None else f"{timeout:g}s",
@@ -1095,7 +1148,7 @@ def _wait_for_signal(
1095
1148
  session_handle,
1096
1149
  )
1097
1150
  try:
1098
- if not result:
1151
+ if not result and not session_closed:
1099
1152
  timeout_source = None
1100
1153
  if timeout is not None:
1101
1154
  timeout_source = GLib.timeout_add(int(timeout * 1000), on_timeout)
@@ -1115,6 +1168,9 @@ def _wait_for_signal(
1115
1168
  GLib.source_remove(timeout_source)
1116
1169
  finally:
1117
1170
  connection.signal_unsubscribe(subscription)
1171
+ connection.signal_unsubscribe(closed_subscription)
1172
+ if session_closed:
1173
+ raise PortalSessionClosedError(signal)
1118
1174
  if timed_out:
1119
1175
  # timed_out is only ever set inside on_timeout, itself only ever
1120
1176
  # registered when timeout is not None -- so this always holds, but
@@ -1176,12 +1232,13 @@ class InputCaptureSession:
1176
1232
  about, and this cannot release on a caller's behalf during cleanup
1177
1233
  without risking racing a capture that only just started.
1178
1234
 
1179
- **Never live-tested.** Every other class in this module that talks to a
1180
- real portal carries a hand-verification note in its own docstring; this
1181
- one does not, because verifying it means a developer clicking through
1182
- the consent dialog *and* accepting that their pointer will be diverted
1183
- away from their own desktop for the length of the test -- not something
1184
- to trigger without asking first, unlike everything else here. Designed
1235
+ **Only half live-tested.** The negotiation -- consent dialog, EIS fd,
1236
+ zones, ``Session.Close()`` -- was run against a real GNOME session on
1237
+ libei's own hand-verification terms (see
1238
+ ``docs/developers/verification.md``); the *capture* half never has been,
1239
+ because verifying it means a developer accepting that their pointer will
1240
+ be diverted away from their own desktop for the length of the test -- not
1241
+ something to trigger without asking first, unlike everything else here. Designed
1185
1242
  against ``/usr/share/dbus-1/interfaces/org.freedesktop.portal.
1186
1243
  InputCapture.xml`` (the shipped portal spec, not the header alone) and
1187
1244
  unit-tested against a fake connection reproducing that spec's documented
@@ -1,18 +1,21 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: python-libei
3
- Version: 0.6.0
4
- Summary: Inject and receive input on Wayland from Python: ctypes bindings for libei, libeis and liboeffis
3
+ Version: 0.7.0
4
+ Summary: Inject, receive and capture input on Wayland from Python: ctypes bindings for libei, libeis and liboeffis, plus the RemoteDesktop and InputCapture portals
5
5
  Author: Dennis K. Paulsen
6
6
  License-Expression: MIT
7
7
  Project-URL: homepage, https://github.com/ctrondlp/python-libei
8
8
  Project-URL: repository, https://github.com/ctrondlp/python-libei
9
9
  Project-URL: issues, https://github.com/ctrondlp/python-libei/issues
10
+ Project-URL: documentation, https://github.com/ctrondlp/python-libei/tree/main/docs
11
+ Project-URL: pyguitest, https://github.com/ctrondlp/pyguitest
10
12
  Project-URL: changelog, https://github.com/ctrondlp/python-libei/blob/main/CHANGELOG.md
11
- Keywords: wayland,libei,libeis,liboeffis,input,input-emulation,emulated-input,portal,xdg-desktop-portal,remote-desktop,automation,gui-testing,accessibility,ctypes
13
+ Keywords: wayland,libei,libeis,liboeffis,input,input-emulation,emulated-input,input-capture,eis,portal,xdg-desktop-portal,remote-desktop,automation,gui-testing,accessibility,ctypes
12
14
  Classifier: Development Status :: 4 - Beta
13
15
  Classifier: Intended Audience :: Developers
14
16
  Classifier: Operating System :: POSIX :: BSD :: FreeBSD
15
17
  Classifier: Operating System :: POSIX :: Linux
18
+ Classifier: Operating System :: POSIX
16
19
  Classifier: Programming Language :: Python :: 3
17
20
  Classifier: Programming Language :: Python :: 3 :: Only
18
21
  Classifier: Programming Language :: Python :: 3.10
@@ -37,6 +40,7 @@ Dynamic: license-file
37
40
 
38
41
  # python-libei
39
42
 
43
+ [![Contributing](https://img.shields.io/badge/contributions-welcome-brightgreen.svg)](https://github.com/ctrondlp/python-libei/blob/main/CONTRIBUTING.md)
40
44
  [![CI](https://github.com/ctrondlp/python-libei/actions/workflows/ci.yml/badge.svg?branch=main)](https://github.com/ctrondlp/python-libei/actions/workflows/ci.yml)
41
45
  [![PyPI](https://img.shields.io/pypi/v/python-libei)](https://pypi.org/project/python-libei/)
42
46
  [![License](https://img.shields.io/pypi/l/python-libei)](https://github.com/ctrondlp/python-libei/blob/main/LICENSE)
@@ -53,8 +57,11 @@ device.start_emulating().pointer_motion(5, 0).frame().stop_emulating()
53
57
  ```
54
58
 
55
59
  **The one concept:** events queue up, and **`frame()` is what sends them** as
56
- one logical hardware event. Forget it and nothing happens — no exception, no
57
- warning, no movement. Fill the package, then post it.
60
+ one logical hardware event. Forget it and the motion is usually lost — no
61
+ exception, no warning, no movement. (Ending the chain with `stop_emulating()`
62
+ makes libei frame the queue for you, but it logs an error-level `Bug:` line
63
+ when it does; see [Things that will bite you](#things-that-will-bite-you).) Fill
64
+ the package, then post it.
58
65
 
59
66
  New here? [docs/getting-started.md](docs/getting-started.md) is install
60
67
  through a first real pointer motion, in five minutes.
@@ -96,8 +103,8 @@ than a widget-tree back door:
96
103
 
97
104
  ## Which API do I need?
98
105
 
99
- Five modules, and most callers need exactly two of them: `oeffis` or `portal`
100
- to get permission, then `ei` to inject.
106
+ Four modules (`ei`, `eis`, `oeffis`, `portal`), and most callers need exactly
107
+ two of them: `oeffis` or `portal` to get permission, then `ei` to inject.
101
108
 
102
109
  | You want to… | Use | Notes |
103
110
  | --- | --- | --- |
@@ -105,6 +112,7 @@ to get permission, then `ei` to inject.
105
112
  | **Consume input** from a compositor | `libei.ei` → `Receiver` | Compositor-side or input-capture code; same connection dance |
106
113
  | **Get permission**, simply | `libei.oeffis` | One call, pollable fd, no dependencies. The consent dialog appears on **every** run |
107
114
  | **Get permission**, and not be asked again | `libei.portal` | Same handshake over D-Bus directly, with `persist_mode` / `restore_token`. Needs PyGObject |
115
+ | **Capture the user's real input** | `libei.portal` → `InputCaptureSession` | The read direction, through the InputCapture portal: once the compositor decides to, the user's pointer and keyboard are diverted to you over EIS. Exclusive while active, so hold it briefly. Only its negotiation half has been run against a real desktop — see [verification](docs/developers/verification.md) |
108
116
  | **Be the server**, for tests or a compositor | `libei.eis` | Drives your client code with no real compositor and no consent dialog |
109
117
 
110
118
  Each module has `is_available()`. `ei` and `eis` share the shapes around them:
@@ -143,8 +151,9 @@ because a skimming reader could otherwise take them as supported. 1.6.0's own
143
151
  `enum ei_device_capability` stops at `TEXT`, and its `enum ei_event_type`
144
152
  stops at `EI_EVENT_TEXT_UTF8` — so the swipe/pinch/hold/stylus members of
145
153
  `EventType` cannot arrive either. The values here match upstream `main`
146
- exactly, so they are ready for whatever release adds them. The 22
147
- gesture/stylus accessor functions `main` adds are deliberately not bound:
154
+ exactly, so they are ready for whatever release adds them. The gesture
155
+ and stylus functions `main` adds -- 46 in libei and 51 in libeis against
156
+ 1.6.0's headers, counted 2026-09-30 -- are deliberately not bound:
148
157
  nothing that ships today exports them, so nothing here could be verified
149
158
  against a real library, which is the bar every other binding in this package
150
159
  was held to.
@@ -166,7 +175,7 @@ and why, is in
166
175
 
167
176
  ## Status
168
177
 
169
- Beta (`0.6.0`), published on [PyPI](https://pypi.org/project/python-libei/)
178
+ Beta (`0.7.0`), published on [PyPI](https://pypi.org/project/python-libei/)
170
179
  since `0.1.0`, and **the API is not frozen** — expect renames before 1.0.
171
180
 
172
181
  The injection path is exercised end to end against the real libraries by the
@@ -207,9 +216,13 @@ Exactly what was run, when, and against which versions:
207
216
  - libei 1.0.0 or newer for the core: connecting, binding a seat, and
208
217
  sending pointer, button, keyboard, scroll and touch input all use symbols
209
218
  that have existed with a stable signature since 1.0.0, and upstream keeps
210
- API/ABI back-compatible within the 1.x series. Only 1.5.0 and 1.6.0 have
211
- actually been run against -- 1.6.0 on both Fedora 44 and FreeBSD 15, where
212
- the injection path passes the full suite with nothing skipped.
219
+ API/ABI back-compatible within the 1.x series. The suite has been run
220
+ against 1.2.1 (Ubuntu 24.04, in CI) and 1.6.0 -- the latter on Fedora 44 and
221
+ 45 and FreeBSD 15, where the injection path passes the full suite with
222
+ nothing skipped. Every binding's name, argument types and return type, and
223
+ every enum value, is also checked against the upstream headers of 1.0.0,
224
+ 1.2.1, 1.4.0, 1.5.0 and 1.6.0 (`tests/test_abi.py`), which is the only
225
+ coverage 1.0.0, 1.4.0 and 1.5.0 have had.
213
226
 
214
227
  Newer libei buys you more, per feature:
215
228
 
@@ -367,8 +380,12 @@ All four, with working code:
367
380
  soon as the loop moves on, and using it afterwards raises `RuntimeError`.
368
381
  Objects you pull *off* an event (`event.device`, `event.seat`) are safe to
369
382
  keep — copy out `event.pointer_event` and friends rather than the event.
370
- - **`frame()` or nothing happens.** Events queue up until a `frame()` commits
371
- them.
383
+ - **`frame()` or the input is lost.** Events queue up until a `frame()` commits
384
+ them, and queued events that are never framed are dropped without an
385
+ exception or a log line. The one exception is a chain that ends in
386
+ `stop_emulating()`: libei frames what is queued, delivers it, and logs
387
+ `Bug: ei_device_stop_emulating: missing call to ei_device_frame()` at error
388
+ level. Measured against libei 1.2.1 and 1.6.0; don't rely on it.
372
389
  - **`bind()` needs at least one capability.** Binding an empty set sends
373
390
  nothing, so the device you are waiting for never arrives; this raises
374
391
  `ValueError` rather than hanging.
@@ -377,20 +394,22 @@ All four, with working code:
377
394
  - **One seat can resume several devices.** Bind both `POINTER` and
378
395
  `POINTER_ABSOLUTE` and GNOME gives you two, relative first. Taking
379
396
  whichever resumes first is a coin flip — select on `device.capabilities`
380
- instead. Sending an event the device lacks the capability for is silently
381
- ignored, which makes this look like the injection simply not working.
397
+ instead. Sending an event the device lacks the capability for is dropped with
398
+ no exception — libei only logs an error-level `Bug: ... device is not a
399
+ keyboard` line — which makes this look like the injection simply not working.
382
400
  - **Read the accessor that matches the event type.** `event.key_event` on a
383
401
  `POINTER_MOTION` event raises `TypeError` naming both types. libei itself
384
402
  would have returned `KeyEvent(key=0, is_press=False)` — a real-looking
385
- value — while logging a `Bug:` line the caller never sees, so branch on
386
- `event_type` first. `TOUCH_UP` has its own `touch_up_event`, since it
403
+ value — and raised nothing, only logging a `Bug:` line at error level, so
404
+ branch on `event_type` first. `TOUCH_UP` has its own `touch_up_event`, since it
387
405
  carries no coordinates.
388
406
  - **`GESTURES` and `STYLUS` are not in any released libei.** They match
389
407
  upstream `main` and are here ready for it, but 1.6.0's capability enum
390
408
  stops at `TEXT`. Binding them against a shipping library silently does
391
409
  nothing — no error, no device, no events.
392
410
 
393
- Nearly all of these fail *silently*, which is why
411
+ Nearly all of these fail without raising — some only log an error-level `Bug:`
412
+ line, some not even that — which is why
394
413
  [docs/troubleshooting.md](docs/troubleshooting.md) is a checklist rather than
395
414
  a list of error messages. Start there when nothing happens.
396
415
 
@@ -0,0 +1,17 @@
1
+ libei/__init__.py,sha256=3Ru2esZsveT5oIlBPJ_6DH8RsRoofVvvf27WZafcOZg,1858
2
+ libei/_cobject.py,sha256=XjbUNJpZ87jAyPWh6dmRslk6cMy831oMRFhWmfjLDFo,13344
3
+ libei/ei.py,sha256=W4akKc_BNVfL0FroSXLGD1Z_L3OmYjZT5QDHNnEtMP8,49367
4
+ libei/eis.py,sha256=X_umAaAAbAMC9d2RdnMReeJZI61WsnKz3cySY5YrGr0,45841
5
+ libei/oeffis.py,sha256=gJHR90s9VhQrgkjWUMDx-lxZ8qKxkttnqbx_xo-7oxs,10035
6
+ libei/portal.py,sha256=Q2GbZBUnKJZ90WJtDtpkcgQbvsCLY6xJwngmYRg5qQ0,72089
7
+ libei/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
8
+ libei/_capi/__init__.py,sha256=raISSWSV9hAcE92DcLCz-vUb1IPcsmxRZaS-iAoQJBE,211
9
+ libei/_capi/libei.py,sha256=qsccHa_komCxtvPYi6AobszhFbEG2qA8QYJJVr8SaGM,11356
10
+ libei/_capi/libeis.py,sha256=C_Uh7DwNncFC8A4wuMhviAj5C_TUmKHtLJYq0Qhhz1E,13142
11
+ libei/_capi/liboeffis.py,sha256=V8jdmJm3qOQUzzNiCAXt4Jr2JY11Y4-w6NIfIxbxGI4,1218
12
+ libei/_capi/loader.py,sha256=xW67uk35ckgeFaheDGrHC05ZM5w_cxBGvX5lG_GUSu8,6625
13
+ python_libei-0.7.0.dist-info/licenses/LICENSE,sha256=l6xbMU6Y-JZDzmBciBj-J5t6h6jNg_Bcyp4bHxDepqU,1074
14
+ python_libei-0.7.0.dist-info/METADATA,sha256=kCY_c4mpI43wAk_72MFq1yyv3VZm8r9NBaexaQzgWCg,22882
15
+ python_libei-0.7.0.dist-info/WHEEL,sha256=YVMoNqKzERt-wjUZwJ33xBGAwnFl-4cqbYkTtWa4itE,91
16
+ python_libei-0.7.0.dist-info/top_level.txt,sha256=_DQXzGjDsUBENI_cNkiOxPB4xi8coCbQS1lq18FMudQ,6
17
+ python_libei-0.7.0.dist-info/RECORD,,
@@ -1,17 +0,0 @@
1
- libei/__init__.py,sha256=ypdFYwFMulRHAB3iYKc2NfCvMRvAq9TUFuwQN9b3PWw,1858
2
- libei/_cobject.py,sha256=XjbUNJpZ87jAyPWh6dmRslk6cMy831oMRFhWmfjLDFo,13344
3
- libei/ei.py,sha256=W4akKc_BNVfL0FroSXLGD1Z_L3OmYjZT5QDHNnEtMP8,49367
4
- libei/eis.py,sha256=X_umAaAAbAMC9d2RdnMReeJZI61WsnKz3cySY5YrGr0,45841
5
- libei/oeffis.py,sha256=gJHR90s9VhQrgkjWUMDx-lxZ8qKxkttnqbx_xo-7oxs,10035
6
- libei/portal.py,sha256=eTYzNkA_65VaUlbSkATiXmOA8c6_uUDle2c2lUgZZHY,69698
7
- libei/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
8
- libei/_capi/__init__.py,sha256=raISSWSV9hAcE92DcLCz-vUb1IPcsmxRZaS-iAoQJBE,211
9
- libei/_capi/libei.py,sha256=gpp42bhqPonhVD2zmFWCl4rdk_lm4XvD_7W2Rw5RsuI,11308
10
- libei/_capi/libeis.py,sha256=jQR0Z0YN9qa71mAcSJ__-UaoF8PsVsBs8OXwjfqstFk,13080
11
- libei/_capi/liboeffis.py,sha256=V8jdmJm3qOQUzzNiCAXt4Jr2JY11Y4-w6NIfIxbxGI4,1218
12
- libei/_capi/loader.py,sha256=QD1xQJo05q4eSWhOCRpAWOlzHD5fcnHtI6yQyy4rKs0,5052
13
- python_libei-0.6.0.dist-info/licenses/LICENSE,sha256=l6xbMU6Y-JZDzmBciBj-J5t6h6jNg_Bcyp4bHxDepqU,1074
14
- python_libei-0.6.0.dist-info/METADATA,sha256=NC7ygbrH78TjzREhLvF7-FSz8kNgf8tPE21NZQiykp4,20963
15
- python_libei-0.6.0.dist-info/WHEEL,sha256=YVMoNqKzERt-wjUZwJ33xBGAwnFl-4cqbYkTtWa4itE,91
16
- python_libei-0.6.0.dist-info/top_level.txt,sha256=_DQXzGjDsUBENI_cNkiOxPB4xi8coCbQS1lq18FMudQ,6
17
- python_libei-0.6.0.dist-info/RECORD,,