Skip to content

Commit 84dd0d9

Browse files
author
Martin Belanger
committed
python bindings: redesign Phase 1 — generator-emitted SWIG fragments
Extend generate-accessors.py to emit three new SWIG fragment files from the existing !generate-accessors annotations in private.h and private-fabrics.h: accessors.i — kernel-object structs (Ctrl, Host, Subsystem, Namespace, GlobalCtx) accessors-fabrics.i — fabrics-specific structs nvme-manual-bridges.i — residual hand-written %rename bridges A new struct-level annotation !generate-python[:alias=NAME] gates emission for Python and carries the PascalCase class alias. Remove the hand-written %rename(ctrl) / %rename(host) / ... lines from nvme.i; they are now generator-emitted. Restructure nvme.i so all %typemap directives precede the generated %include "accessors.i". SWIG freezes type-to-typemap associations when it first processes a struct body; struct bodies now arrive via the generated include so typemaps must come first. A generator-emitted _nvme_guarded_setattr is installed on each class at import to catch typos and writes to read-only (%immutable) properties. C-internal iteration helpers are renamed with a leading underscore via %rename to keep them out of the public API surface. Update nvme.i docstrings to Pythonic style (Args:/Returns: conventions, PascalCase class names, module-level usage examples). Update tests and examples to use the new API. Verified: meson test -C .build passes (64 OK, 2 expected failures). Signed-off-by: Martin Belanger <[email protected]> Assisted-by: Claude Sonnet 4.6 <[email protected]>
1 parent 2e7dcff commit 84dd0d9

19 files changed

Lines changed: 1683 additions & 1356 deletions

libnvme/examples/discover-loop.py

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ def disc_supp_str(dlp_supp_opts):
1616
}
1717
return [txt for msk, txt in d.items() if dlp_supp_opts & msk]
1818

19-
def discover(host, ctrl, iteration):
19+
def discover(ctx, host, ctrl, iteration):
2020
# Only 8 levels of indirection are supported
2121
if iteration > 8:
2222
return
@@ -51,19 +51,18 @@ def discover(host, ctrl, iteration):
5151
if dlpe['subtype'] == 'discovery' and dlpe['subnqn'] == nvme.NVME_DISC_SUBSYS_NAME:
5252
continue
5353
print(f'{iteration}: {dlpe["subtype"]} {dlpe["subnqn"]}')
54-
with nvme.ctrl(root, subsysnqn=dlpe['subnqn'], transport=dlpe['trtype'], traddr=dlpe['traddr'], trsvcid=dlpe['trsvcid']) as new_ctrl:
54+
with nvme.Ctrl(ctx, subsysnqn=dlpe['subnqn'], transport=dlpe['trtype'], traddr=dlpe['traddr'], trsvcid=dlpe['trsvcid']) as new_ctrl:
5555
discover(host, new_ctrl, iteration + 1)
5656

57-
root = nvme.root()
58-
host = nvme.host(root)
59-
57+
ctx = nvme.GlobalCtx()
58+
host = nvme.Host(ctx)
6059
subsysnqn = nvme.NVME_DISC_SUBSYS_NAME
6160
transport = 'tcp'
6261
traddr = '127.0.0.1'
6362
trsvcid = '4420'
6463

65-
with nvme.ctrl(root, subsysnqn=subsysnqn, transport=transport, traddr=traddr, trsvcid=trsvcid) as ctrl:
66-
discover(host, ctrl, 0)
64+
with nvme.Ctrl(ctx, subsysnqn=subsysnqn, transport=transport, traddr=traddr, trsvcid=trsvcid) as ctrl:
65+
discover(ctx, host, ctrl, 0)
6766

6867
for s in host.subsystems():
6968
for c in s.controllers():
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
// SPDX-License-Identifier: LGPL-2.1-or-later
2+
3+
/*
4+
* This file is part of libnvme.
5+
*
6+
* Copyright (c) 2025, Dell Technologies Inc. or its subsidiaries.
7+
* Authors: Martin Belanger <[email protected]>
8+
*
9+
* ____ _ _ ____ _
10+
* / ___| ___ _ __ ___ _ __ __ _| |_ ___ __| | / ___|___ __| | ___
11+
* | | _ / _ \ '_ \ / _ \ '__/ _` | __/ _ \/ _` | | | / _ \ / _` |/ _ \
12+
* | |_| | __/ | | | __/ | | (_| | || __/ (_| | | |__| (_) | (_| | __/
13+
* \____|\___|_| |_|\___|_| \__,_|\__\___|\__,_| \____\___/ \__,_|\___|
14+
*
15+
* Auto-generated struct member accessors (setter/getter)
16+
*
17+
* To update run: meson compile -C [BUILD-DIR] update-accessors
18+
* Or: make update-accessors
19+
*/

libnvme/libnvme/accessors.i

Lines changed: 217 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,217 @@
1+
// SPDX-License-Identifier: LGPL-2.1-or-later
2+
3+
/*
4+
* This file is part of libnvme.
5+
*
6+
* Copyright (c) 2025, Dell Technologies Inc. or its subsidiaries.
7+
* Authors: Martin Belanger <[email protected]>
8+
*
9+
* ____ _ _ ____ _
10+
* / ___| ___ _ __ ___ _ __ __ _| |_ ___ __| | / ___|___ __| | ___
11+
* | | _ / _ \ '_ \ / _ \ '__/ _` | __/ _ \/ _` | | | / _ \ / _` |/ _ \
12+
* | |_| | __/ | | | __/ | | (_| | || __/ (_| | | |__| (_) | (_| | __/
13+
* \____|\___|_| |_|\___|_| \__,_|\__\___|\__,_| \____\___/ \__,_|\___|
14+
*
15+
* Auto-generated struct member accessors (setter/getter)
16+
*
17+
* To update run: meson compile -C [BUILD-DIR] update-accessors
18+
* Or: make update-accessors
19+
*/
20+
%pythoncode %{
21+
def _nvme_guarded_setattr(self, name, value):
22+
"""Reject writes to unknown attributes.
23+
24+
Typos like ``ctrl.nqn = x`` (should be ``ctrl.subsysnqn``) are
25+
silently ignored by default Python ``__setattr__``. This guard
26+
raises ``AttributeError`` for any name not already present on the
27+
object, keeping the struct-like API strict.
28+
"""
29+
if name.startswith('_') or name in ('this', 'thisown') or hasattr(type(self), name):
30+
object.__setattr__(self, name, value)
31+
else:
32+
raise AttributeError(
33+
f"{type(self).__name__!r} has no attribute {name!r}")
34+
%}
35+
36+
/* struct libnvme_ns */
37+
%rename(Namespace) libnvme_ns;
38+
%rename(libnvme_ns_command_retry_count_get) libnvme_ns_get_command_retry_count;
39+
%rename(libnvme_ns_command_error_count_get) libnvme_ns_get_command_error_count;
40+
%rename(libnvme_ns_requeue_no_usable_path_count_get) libnvme_ns_get_requeue_no_usable_path_count;
41+
%rename(libnvme_ns_fail_no_available_path_count_get) libnvme_ns_get_fail_no_available_path_count;
42+
%{
43+
#define libnvme_ns_command_retry_count_get libnvme_ns_get_command_retry_count
44+
#define libnvme_ns_command_error_count_get libnvme_ns_get_command_error_count
45+
#define libnvme_ns_requeue_no_usable_path_count_get libnvme_ns_get_requeue_no_usable_path_count
46+
#define libnvme_ns_fail_no_available_path_count_get libnvme_ns_get_fail_no_available_path_count
47+
%}
48+
struct libnvme_ns {
49+
__u32 nsid;
50+
%immutable name;
51+
const char * name;
52+
%immutable generic_name;
53+
const char * generic_name;
54+
const char * sysfs_dir;
55+
int lba_shift;
56+
int lba_size;
57+
int meta_size;
58+
uint64_t lba_count;
59+
uint64_t lba_util;
60+
%immutable eui64;
61+
uint8_t eui64[8];
62+
%immutable nguid;
63+
uint8_t nguid[16];
64+
%immutable csi;
65+
enum nvme_csi csi;
66+
%extend {
67+
%immutable command_retry_count;
68+
long command_retry_count;
69+
%immutable command_error_count;
70+
long command_error_count;
71+
%immutable requeue_no_usable_path_count;
72+
long requeue_no_usable_path_count;
73+
%immutable fail_no_available_path_count;
74+
long fail_no_available_path_count;
75+
}
76+
};
77+
78+
%pythoncode %{
79+
Namespace.__setattr__ = _nvme_guarded_setattr
80+
%}
81+
82+
/* struct libnvme_ctrl */
83+
%rename(Ctrl) libnvme_ctrl;
84+
%rename(libnvme_ctrl_state_get) libnvme_ctrl_get_state;
85+
%rename(libnvme_ctrl_command_error_count_get) libnvme_ctrl_get_command_error_count;
86+
%rename(libnvme_ctrl_reset_count_get) libnvme_ctrl_get_reset_count;
87+
%rename(libnvme_ctrl_reconnect_count_get) libnvme_ctrl_get_reconnect_count;
88+
%{
89+
#define libnvme_ctrl_state_get libnvme_ctrl_get_state
90+
#define libnvme_ctrl_command_error_count_get libnvme_ctrl_get_command_error_count
91+
#define libnvme_ctrl_reset_count_get libnvme_ctrl_get_reset_count
92+
#define libnvme_ctrl_reconnect_count_get libnvme_ctrl_get_reconnect_count
93+
%}
94+
struct libnvme_ctrl {
95+
%immutable name;
96+
const char * name;
97+
%immutable sysfs_dir;
98+
const char * sysfs_dir;
99+
%immutable address;
100+
const char * address;
101+
%immutable firmware;
102+
const char * firmware;
103+
%immutable model;
104+
const char * model;
105+
%immutable numa_node;
106+
const char * numa_node;
107+
%immutable queue_count;
108+
const char * queue_count;
109+
%immutable serial;
110+
const char * serial;
111+
%immutable sqsize;
112+
const char * sqsize;
113+
%immutable transport;
114+
const char * transport;
115+
%immutable subsysnqn;
116+
const char * subsysnqn;
117+
%immutable traddr;
118+
const char * traddr;
119+
%immutable trsvcid;
120+
const char * trsvcid;
121+
const char * dhchap_host_key;
122+
const char * dhchap_ctrl_key;
123+
const char * keyring;
124+
const char * tls_key_identity;
125+
const char * tls_key;
126+
%immutable cntrltype;
127+
const char * cntrltype;
128+
%immutable cntlid;
129+
const char * cntlid;
130+
%immutable dctype;
131+
const char * dctype;
132+
%immutable phy_slot;
133+
const char * phy_slot;
134+
%immutable host_traddr;
135+
const char * host_traddr;
136+
%immutable host_iface;
137+
const char * host_iface;
138+
bool discovery_ctrl;
139+
bool unique_discovery_ctrl;
140+
bool discovered;
141+
bool persistent;
142+
%extend {
143+
%immutable state;
144+
const char * state;
145+
%immutable command_error_count;
146+
long command_error_count;
147+
%immutable reset_count;
148+
long reset_count;
149+
%immutable reconnect_count;
150+
long reconnect_count;
151+
}
152+
};
153+
154+
%pythoncode %{
155+
Ctrl.__setattr__ = _nvme_guarded_setattr
156+
%}
157+
158+
/* struct libnvme_subsystem */
159+
%rename(Subsystem) libnvme_subsystem;
160+
%rename(libnvme_subsystem_iopolicy_get) libnvme_subsystem_get_iopolicy;
161+
%{
162+
#define libnvme_subsystem_iopolicy_get libnvme_subsystem_get_iopolicy
163+
%}
164+
struct libnvme_subsystem {
165+
%immutable name;
166+
const char * name;
167+
%immutable sysfs_dir;
168+
const char * sysfs_dir;
169+
%immutable subsysnqn;
170+
const char * subsysnqn;
171+
%immutable model;
172+
const char * model;
173+
%immutable serial;
174+
const char * serial;
175+
%immutable firmware;
176+
const char * firmware;
177+
%immutable subsystype;
178+
const char * subsystype;
179+
const char * application;
180+
%extend {
181+
%immutable iopolicy;
182+
const char * iopolicy;
183+
}
184+
};
185+
186+
%pythoncode %{
187+
Subsystem.__setattr__ = _nvme_guarded_setattr
188+
%}
189+
190+
/* struct libnvme_host */
191+
%rename(Host) libnvme_host;
192+
%rename(libnvme_host_pdc_enabled_set) libnvme_host_set_pdc_enabled;
193+
%{
194+
#define libnvme_host_pdc_enabled_set libnvme_host_set_pdc_enabled
195+
%}
196+
struct libnvme_host {
197+
%immutable hostnqn;
198+
const char * hostnqn;
199+
%immutable hostid;
200+
const char * hostid;
201+
const char * dhchap_host_key;
202+
const char * hostsymname;
203+
};
204+
205+
%pythoncode %{
206+
Host.__setattr__ = _nvme_guarded_setattr
207+
%}
208+
209+
/* struct libnvme_global_ctx */
210+
%rename(GlobalCtx) libnvme_global_ctx;
211+
struct libnvme_global_ctx {
212+
};
213+
214+
%pythoncode %{
215+
GlobalCtx.__setattr__ = _nvme_guarded_setattr
216+
%}
217+

libnvme/libnvme/meson.build

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,11 @@ if want_python
2323
input: ['nvme.i'],
2424
output: ['nvme.py', 'nvme_wrap.c'],
2525
command: swig_cmd,
26+
depend_files: [
27+
'accessors.i',
28+
'accessors-fabrics.i',
29+
'nvme-manual-bridges.i',
30+
],
2631
install: true,
2732
install_dir: [python3.get_install_dir(pure: false, subdir: 'libnvme'), false],
2833
)
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
// SPDX-License-Identifier: LGPL-2.1-or-later
2+
3+
/*
4+
* This file is part of libnvme.
5+
*
6+
* Copyright (c) 2025, Dell Technologies Inc. or its subsidiaries.
7+
* Authors: Martin Belanger <[email protected]>
8+
*
9+
* Hand-maintained SWIG accessor bridges.
10+
*
11+
* These members expose nested struct pointers and cannot be expressed
12+
* as generated accessors — they must be kept here, manually.
13+
*/
14+
15+
/* ctrl.subsystem: exposes the parent libnvme_subsystem pointer */
16+
%rename(libnvme_ctrl_subsystem_get) libnvme_ctrl_get_subsystem;
17+
18+
/* subsystem.host: exposes the parent libnvme_host pointer */
19+
%rename(libnvme_subsystem_host_get) libnvme_subsystem_get_host;
20+
21+
%{
22+
#define libnvme_ctrl_subsystem_get libnvme_ctrl_get_subsystem
23+
#define libnvme_subsystem_host_get libnvme_subsystem_get_host
24+
%}

0 commit comments

Comments
 (0)