omlish-cext 0.0.0.dev508__tar.gz → 0.0.0.dev509__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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: omlish-cext
3
- Version: 0.0.0.dev508
3
+ Version: 0.0.0.dev509
4
4
  Summary: omlish
5
5
  Author: wrmsr
6
6
  License-Expression: BSD-3-Clause
@@ -14,7 +14,7 @@ Classifier: Programming Language :: Python :: 3.13
14
14
  Requires-Python: >=3.13
15
15
  Description-Content-Type: text/markdown
16
16
  License-File: LICENSE
17
- Requires-Dist: omlish==0.0.0.dev508
17
+ Requires-Dist: omlish==0.0.0.dev509
18
18
  Dynamic: license-file
19
19
 
20
20
  # Overview
@@ -30,11 +30,51 @@ static inline collection_state * get_collection_state(PyObject *module)
30
30
 
31
31
  //
32
32
 
33
+ template<typename T = PyObject>
34
+ class PyRef {
35
+ T* ptr_;
36
+ public:
37
+ explicit PyRef(T* ptr = nullptr) : ptr_(ptr) {}
38
+ ~PyRef() { Py_XDECREF(reinterpret_cast<PyObject*>(ptr_)); }
39
+
40
+ // No copy
41
+ PyRef(const PyRef&) = delete;
42
+ PyRef& operator=(const PyRef&) = delete;
43
+
44
+ // Move is OK
45
+ PyRef(PyRef&& other) noexcept : ptr_(other.release()) {}
46
+ PyRef& operator=(PyRef&& other) noexcept {
47
+ if (this != &other) {
48
+ reset(other.release());
49
+ }
50
+ return *this;
51
+ }
52
+
53
+ void reset(T* ptr = nullptr) {
54
+ PyObject* old = reinterpret_cast<PyObject*>(ptr_);
55
+ ptr_ = ptr;
56
+ Py_XDECREF(old);
57
+ }
58
+
59
+ T* get() const { return ptr_; }
60
+
61
+ // Explicitly give up ownership
62
+ [[nodiscard]] T* release() {
63
+ T* p = ptr_;
64
+ ptr_ = nullptr;
65
+ return p;
66
+ }
67
+
68
+ explicit operator bool() const { return ptr_ != nullptr; }
69
+
70
+ operator T*() const = delete;
71
+ };
72
+
33
73
  // Helper struct to track unique typed values during processing
34
74
  struct UniqueInfo {
35
- PyObject *unique_tv_cls; // The unique class (borrowed from map key)
36
- PyObject *tv; // The typed value (borrowed from args)
37
- size_t idx; // Index in unique_lst when added
75
+ PyObject *unique_tv_cls; // The unique class (borrowed from map key)
76
+ PyObject *tv; // The typed value (borrowed from args)
77
+ size_t idx; // Index in unique_lst when added
38
78
  };
39
79
 
40
80
  using TmpItem = std::variant<PyObject*, std::unique_ptr<UniqueInfo>>;
@@ -85,29 +125,22 @@ static PyObject * init_typed_values_collection(PyObject *module, PyObject *const
85
125
 
86
126
  // If no positional arguments, return empty tuple and dicts
87
127
  if (nargs == 0) {
88
- PyObject *empty_tuple = PyTuple_New(0);
89
- if (empty_tuple == nullptr) {
128
+ PyRef empty_tuple(PyTuple_New(0));
129
+ if (!empty_tuple) {
90
130
  return nullptr;
91
131
  }
92
132
 
93
- PyObject *empty_dict = PyDict_New();
94
- if (empty_dict == nullptr) {
95
- Py_DECREF(empty_tuple);
133
+ PyRef empty_dict(PyDict_New());
134
+ if (!empty_dict) {
96
135
  return nullptr;
97
136
  }
98
137
 
99
- PyObject *empty_dict2 = PyDict_New();
100
- if (empty_dict2 == nullptr) {
101
- Py_DECREF(empty_tuple);
102
- Py_DECREF(empty_dict);
138
+ PyRef empty_dict2(PyDict_New());
139
+ if (!empty_dict2) {
103
140
  return nullptr;
104
141
  }
105
142
 
106
- PyObject *result = PyTuple_Pack(3, empty_tuple, empty_dict, empty_dict2);
107
- Py_DECREF(empty_tuple);
108
- Py_DECREF(empty_dict);
109
- Py_DECREF(empty_dict2);
110
- return result;
143
+ return PyTuple_Pack(3, empty_tuple.get(), empty_dict.get(), empty_dict2.get());
111
144
  }
112
145
 
113
146
  // Temporary storage
@@ -146,53 +179,51 @@ static PyObject * init_typed_values_collection(PyObject *module, PyObject *const
146
179
 
147
180
  if (is_unique) {
148
181
  // Get the _unique_typed_value_cls attribute
149
- PyObject *unique_tv_cls = PyObject_GetAttrString(tv, "_unique_typed_value_cls");
150
- if (unique_tv_cls == nullptr) {
182
+ PyRef unique_tv_cls(PyObject_GetAttrString(tv, "_unique_typed_value_cls"));
183
+ if (!unique_tv_cls) {
151
184
  return nullptr;
152
185
  }
153
186
 
154
187
  // Check for duplicates if not override
155
188
  if (!override) {
156
- auto it = unique_dct.find(unique_tv_cls);
189
+ auto it = unique_dct.find(unique_tv_cls.get());
157
190
  if (it != unique_dct.end() && !it->second.empty()) {
158
191
  // Duplicate found - raise error
159
192
  PyObject *old_tv = it->second[0];
160
193
 
161
194
  // Create DuplicateUniqueTypedValueError
162
- PyObject *error = PyObject_CallFunction(
195
+ PyRef error(PyObject_CallFunction(
163
196
  state->duplicate_error_type,
164
197
  "OOO",
165
- unique_tv_cls,
198
+ unique_tv_cls.get(),
166
199
  tv,
167
200
  old_tv
168
- );
201
+ ));
169
202
 
170
- Py_DECREF(unique_tv_cls);
171
-
172
- if (error == nullptr) {
203
+ if (!error) {
173
204
  return nullptr;
174
205
  }
175
206
 
176
- PyErr_SetObject(state->duplicate_error_type, error);
177
- Py_DECREF(error);
207
+ PyErr_SetObject(state->duplicate_error_type, error.get());
178
208
  return nullptr;
179
209
  }
180
210
  }
181
211
 
182
212
  // Add to unique_dct
183
213
  // Use insert to avoid reference leak: if key already exists, we need to decref the new reference
184
- auto insertion = unique_dct.insert({unique_tv_cls, std::vector<PyObject*>()});
214
+ auto insertion = unique_dct.insert({unique_tv_cls.get(), std::vector<PyObject*>()});
185
215
  if (!insertion.second) {
186
- // Key already existed, decref the newly acquired reference
187
- Py_DECREF(unique_tv_cls);
216
+ // Key already existed, PyRef will automatically decref
217
+ } else {
218
+ // Key was inserted, transfer ownership to map using .release()
219
+ (void)unique_tv_cls.release();
188
220
  }
189
221
  std::vector<PyObject*> &unique_lst = insertion.first->second;
190
222
  unique_lst.push_back(tv);
191
223
 
192
224
  // Add to tmp_lst using make_unique
193
- // unique_ptr automatically handles deletion, variant handles moves
194
225
  tmp_lst.emplace_back(std::make_unique<UniqueInfo>(UniqueInfo{
195
- unique_tv_cls, // Borrowed from map
226
+ insertion.first->first, // Borrowed from map
196
227
  tv,
197
228
  unique_lst.size()
198
229
  }));
@@ -215,14 +246,13 @@ static PyObject * init_typed_values_collection(PyObject *module, PyObject *const
215
246
  }
216
247
 
217
248
  // Build output structures
218
- PyObject *lst = PyList_New(0);
219
- if (lst == nullptr) {
249
+ PyRef lst(PyList_New(0));
250
+ if (!lst) {
220
251
  return nullptr;
221
252
  }
222
253
 
223
- PyObject *tmp_dct = PyDict_New();
224
- if (tmp_dct == nullptr) {
225
- Py_DECREF(lst);
254
+ PyRef tmp_dct(PyDict_New());
255
+ if (!tmp_dct) {
226
256
  return nullptr;
227
257
  }
228
258
 
@@ -239,16 +269,12 @@ static PyObject * init_typed_values_collection(PyObject *module, PyObject *const
239
269
  // Last-in-wins: only include if this is the last one
240
270
  if (info->idx == it->second.size()) {
241
271
  // Add to output list
242
- if (PyList_Append(lst, info->tv) == -1) {
243
- Py_DECREF(lst);
244
- Py_DECREF(tmp_dct);
272
+ if (PyList_Append(lst.get(), info->tv) == -1) {
245
273
  return nullptr;
246
274
  }
247
275
 
248
276
  // Add to tmp_dct (scalar value for unique types)
249
- if (PyDict_SetItem(tmp_dct, info->unique_tv_cls, info->tv) == -1) {
250
- Py_DECREF(lst);
251
- Py_DECREF(tmp_dct);
277
+ if (PyDict_SetItem(tmp_dct.get(), info->unique_tv_cls, info->tv) == -1) {
252
278
  return nullptr;
253
279
  }
254
280
  }
@@ -257,52 +283,36 @@ static PyObject * init_typed_values_collection(PyObject *module, PyObject *const
257
283
  PyObject *tv = std::get<PyObject*>(item);
258
284
 
259
285
  // Add to output list
260
- if (PyList_Append(lst, tv) == -1) {
261
- Py_DECREF(lst);
262
- Py_DECREF(tmp_dct);
286
+ if (PyList_Append(lst.get(), tv) == -1) {
263
287
  return nullptr;
264
288
  }
265
289
 
266
290
  // Add to tmp_dct (accumulating list for non-unique types)
267
291
  PyObject *tv_type = (PyObject *)Py_TYPE(tv);
268
- PyObject *existing = PyDict_GetItem(tmp_dct, tv_type); // Borrowed reference
292
+ PyObject *existing = PyDict_GetItem(tmp_dct.get(), tv_type); // Borrowed reference
269
293
 
270
294
  if (existing == nullptr) {
271
295
  // Check if an error occurred during lookup
272
296
  if (PyErr_Occurred()) {
273
- Py_DECREF(lst);
274
- Py_DECREF(tmp_dct);
275
297
  return nullptr;
276
298
  }
277
299
 
278
300
  // Create new list
279
- PyObject *new_list = PyList_New(0);
280
- if (new_list == nullptr) {
281
- Py_DECREF(lst);
282
- Py_DECREF(tmp_dct);
301
+ PyRef new_list(PyList_New(0));
302
+ if (!new_list) {
283
303
  return nullptr;
284
304
  }
285
305
 
286
- if (PyList_Append(new_list, tv) == -1) {
287
- Py_DECREF(new_list);
288
- Py_DECREF(lst);
289
- Py_DECREF(tmp_dct);
306
+ if (PyList_Append(new_list.get(), tv) == -1) {
290
307
  return nullptr;
291
308
  }
292
309
 
293
- if (PyDict_SetItem(tmp_dct, tv_type, new_list) == -1) {
294
- Py_DECREF(new_list);
295
- Py_DECREF(lst);
296
- Py_DECREF(tmp_dct);
310
+ if (PyDict_SetItem(tmp_dct.get(), tv_type, new_list.get()) == -1) {
297
311
  return nullptr;
298
312
  }
299
-
300
- Py_DECREF(new_list);
301
313
  } else {
302
314
  // Append to existing list
303
315
  if (PyList_Append(existing, tv) == -1) {
304
- Py_DECREF(lst);
305
- Py_DECREF(tmp_dct);
306
316
  return nullptr;
307
317
  }
308
318
  }
@@ -310,83 +320,57 @@ static PyObject * init_typed_values_collection(PyObject *module, PyObject *const
310
320
  }
311
321
 
312
322
  // Convert list to tuple
313
- PyObject *result_tuple = PyList_AsTuple(lst);
314
- Py_DECREF(lst);
315
- if (result_tuple == nullptr) {
316
- Py_DECREF(tmp_dct);
323
+ PyRef result_tuple(PyList_AsTuple(lst.get()));
324
+ if (!result_tuple) {
317
325
  return nullptr;
318
326
  }
319
327
 
320
328
  // Build dct: convert lists to tuples
321
- PyObject *dct = PyDict_New();
322
- if (dct == nullptr) {
323
- Py_DECREF(result_tuple);
324
- Py_DECREF(tmp_dct);
329
+ PyRef dct(PyDict_New());
330
+ if (!dct) {
325
331
  return nullptr;
326
332
  }
327
333
 
328
334
  PyObject *key, *value;
329
335
  Py_ssize_t pos = 0;
330
336
 
331
- while (PyDict_Next(tmp_dct, &pos, &key, &value)) {
337
+ while (PyDict_Next(tmp_dct.get(), &pos, &key, &value)) {
332
338
  if (PyList_Check(value)) {
333
- PyObject *tuple_value = PyList_AsTuple(value);
334
- if (tuple_value == nullptr) {
335
- Py_DECREF(result_tuple);
336
- Py_DECREF(tmp_dct);
337
- Py_DECREF(dct);
339
+ PyRef tuple_value(PyList_AsTuple(value));
340
+ if (!tuple_value) {
338
341
  return nullptr;
339
342
  }
340
343
 
341
- if (PyDict_SetItem(dct, key, tuple_value) == -1) {
342
- Py_DECREF(tuple_value);
343
- Py_DECREF(result_tuple);
344
- Py_DECREF(tmp_dct);
345
- Py_DECREF(dct);
344
+ if (PyDict_SetItem(dct.get(), key, tuple_value.get()) == -1) {
346
345
  return nullptr;
347
346
  }
348
-
349
- Py_DECREF(tuple_value);
350
347
  } else {
351
348
  // Scalar value (unique type)
352
- if (PyDict_SetItem(dct, key, value) == -1) {
353
- Py_DECREF(result_tuple);
354
- Py_DECREF(tmp_dct);
355
- Py_DECREF(dct);
349
+ if (PyDict_SetItem(dct.get(), key, value) == -1) {
356
350
  return nullptr;
357
351
  }
358
352
  }
359
353
  }
360
354
 
361
- Py_DECREF(tmp_dct);
362
-
363
355
  // Build dct2: dct + unique values keyed by instance type
364
- PyObject *dct2 = PyDict_Copy(dct);
365
- if (dct2 == nullptr) {
366
- Py_DECREF(result_tuple);
367
- Py_DECREF(dct);
356
+ PyRef dct2(PyDict_Copy(dct.get()));
357
+ if (!dct2) {
368
358
  return nullptr;
369
359
  }
370
360
 
371
361
  // Add unique values by their instance type
372
362
  pos = 0;
373
- while (PyDict_Next(dct, &pos, &key, &value)) {
363
+ while (PyDict_Next(dct.get(), &pos, &key, &value)) {
374
364
  // Check if value is a UniqueTypedValue (not a tuple)
375
365
  if (!PyTuple_Check(value)) {
376
366
  int is_unique = PyObject_IsInstance(value, state->unique_typed_value_type);
377
367
  if (is_unique == -1) {
378
- Py_DECREF(result_tuple);
379
- Py_DECREF(dct);
380
- Py_DECREF(dct2);
381
368
  return nullptr;
382
369
  }
383
370
 
384
371
  if (is_unique) {
385
372
  PyObject *value_type = (PyObject *)Py_TYPE(value);
386
- if (PyDict_SetItem(dct2, value_type, value) == -1) {
387
- Py_DECREF(result_tuple);
388
- Py_DECREF(dct);
389
- Py_DECREF(dct2);
373
+ if (PyDict_SetItem(dct2.get(), value_type, value) == -1) {
390
374
  return nullptr;
391
375
  }
392
376
  }
@@ -394,12 +378,7 @@ static PyObject * init_typed_values_collection(PyObject *module, PyObject *const
394
378
  }
395
379
 
396
380
  // Build final result
397
- PyObject *result = PyTuple_Pack(3, result_tuple, dct, dct2);
398
- Py_DECREF(result_tuple);
399
- Py_DECREF(dct);
400
- Py_DECREF(dct2);
401
-
402
- return result;
381
+ return PyTuple_Pack(3, result_tuple.get(), dct.get(), dct2.get());
403
382
  }
404
383
 
405
384
  //
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: omlish-cext
3
- Version: 0.0.0.dev508
3
+ Version: 0.0.0.dev509
4
4
  Summary: omlish
5
5
  Author: wrmsr
6
6
  License-Expression: BSD-3-Clause
@@ -14,7 +14,7 @@ Classifier: Programming Language :: Python :: 3.13
14
14
  Requires-Python: >=3.13
15
15
  Description-Content-Type: text/markdown
16
16
  License-File: LICENSE
17
- Requires-Dist: omlish==0.0.0.dev508
17
+ Requires-Dist: omlish==0.0.0.dev509
18
18
  Dynamic: license-file
19
19
 
20
20
  # Overview
@@ -0,0 +1 @@
1
+ omlish==0.0.0.dev509
@@ -13,7 +13,7 @@ urls = {source = 'https://github.com/wrmsr/omlish'}
13
13
  license = 'BSD-3-Clause'
14
14
  readme = 'README.md'
15
15
  requires-python = '>=3.13'
16
- version = '0.0.0.dev508'
16
+ version = '0.0.0.dev509'
17
17
  classifiers = [
18
18
  'Development Status :: 2 - Pre-Alpha',
19
19
  'Intended Audience :: Developers',
@@ -24,7 +24,7 @@ classifiers = [
24
24
  ]
25
25
  description = 'omlish'
26
26
  dependencies = [
27
- 'omlish == 0.0.0.dev508',
27
+ 'omlish == 0.0.0.dev509',
28
28
  ]
29
29
 
30
30
  [tool.setuptools]
@@ -1 +0,0 @@
1
- omlish==0.0.0.dev508