winrt-runtime 2.1.0__tar.gz → 2.2.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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: winrt-runtime
3
- Version: 2.1.0
3
+ Version: 2.2.0
4
4
  Summary: Python projection of Windows Runtime (WinRT) APIs
5
5
  License: MIT
6
6
  Project-URL: Documentation, https://pywinrt.readthedocs.io
@@ -10,7 +10,7 @@ Classifier: Operating System :: Microsoft :: Windows
10
10
  Classifier: Programming Language :: Python :: 3
11
11
  Classifier: Programming Language :: Python :: Implementation :: CPython
12
12
  Classifier: Intended Audience :: Developers
13
- Requires-Python: <3.13,>=3.9
13
+ Requires-Python: <3.14,>=3.9
14
14
  Description-Content-Type: text/markdown
15
15
 
16
16
  # winrt-runtime
@@ -1,8 +1,10 @@
1
1
  // must be included before winrt to avoid compile errors
2
2
  #include <Shobjidl.h>
3
+ #include <libloaderapi.h>
3
4
 
4
5
  #define PYWINRT_RUNTIME_MODULE
5
6
  #include "pybase.h"
7
+ #include "pyruntime.h"
6
8
  #include <winrt/base.h>
7
9
 
8
10
  namespace py::cpp::_winrt
@@ -128,6 +130,7 @@ namespace py::cpp::_winrt
128
130
  {
129
131
  PyObject_HEAD;
130
132
  PyObject* _iter;
133
+ PyObject* _key;
131
134
  };
132
135
 
133
136
  static PyMemberDef MappingIter_members[]
@@ -136,6 +139,7 @@ namespace py::cpp::_winrt
136
139
  offsetof(MappingIter_object, _iter),
137
140
  0,
138
141
  PyDoc_STR("base KeyValuePair iterator")},
142
+ {"_key", T_OBJECT, offsetof(MappingIter_object, _key), 0, nullptr},
139
143
  {}};
140
144
 
141
145
  static int MappingIter_init(PyObject* self, PyObject* args, PyObject* kwds) noexcept
@@ -165,30 +169,41 @@ namespace py::cpp::_winrt
165
169
  return -1;
166
170
  }
167
171
 
172
+ py::pyobj_handle key{PyUnicode_FromString("key")};
173
+ if (!key)
174
+ {
175
+ return -1;
176
+ }
177
+
178
+ if (PyObject_SetAttrString(self, "_key", key.get()) == -1)
179
+ {
180
+ return -1;
181
+ }
182
+
168
183
  return 0;
169
184
  }
170
185
 
171
- static PyObject* MappingIter_iternext(PyObject* self) noexcept
186
+ static PyObject* MappingIter_iternext(MappingIter_object* self) noexcept
172
187
  {
173
- // new reference
174
- py::pyobj_handle base_iter{PyObject_GetAttrString(self, "_iter")};
175
-
176
- if (!base_iter)
188
+ if (!self->_iter)
177
189
  {
190
+ PyErr_SetString(PyExc_RuntimeError, "_iter was deleted");
178
191
  return nullptr;
179
192
  }
180
193
 
181
- // new reference
182
- py::pyobj_handle next{PyIter_Next(base_iter.get())};
183
-
194
+ py::pyobj_handle next{PyIter_Next(self->_iter)};
184
195
  if (!next)
185
196
  {
186
197
  return nullptr;
187
198
  }
188
199
 
189
- // new reference
190
- py::pyobj_handle key{PyObject_GetAttrString(next.get(), "key")};
200
+ if (!self->_key)
201
+ {
202
+ PyErr_SetString(PyExc_RuntimeError, "_key was deleted");
203
+ return nullptr;
204
+ }
191
205
 
206
+ py::pyobj_handle key{PyObject_GetAttr(next.get(), self->_key)};
192
207
  if (!key)
193
208
  {
194
209
  return nullptr;
@@ -222,10 +237,13 @@ namespace py::cpp::_winrt
222
237
  .abi_version_major = py::runtime_abi_version_major,
223
238
  .abi_version_minor = py::runtime_abi_version_minor,
224
239
  .register_python_type = py::register_python_type,
240
+ .get_python_type = py::get_python_type,
225
241
  .wrap_mapping_iter = py::wrap_mapping_iter,
226
242
  .is_buffer_compatible = py::is_buffer_compatible,
227
243
  .convert_datetime = py::convert_datetime,
228
244
  .convert_to_datetime = py::convert_to_datetime,
245
+ .convert_guid = py::convert_guid,
246
+ .convert_to_guid = py::convert_to_guid,
229
247
  .get_object_type = py::get_object_type,
230
248
  .array_new = py::cpp::_winrt::Array_New,
231
249
  .array_assign = &py::cpp::_winrt::Array_Assign,
@@ -291,6 +309,148 @@ namespace py::cpp::_winrt
291
309
  Py_RETURN_NONE;
292
310
  }
293
311
 
312
+ static PyObject* add_dll_directory(PyObject* /*unused*/, PyObject* obj) noexcept
313
+ {
314
+ std::unique_ptr<wchar_t, decltype(&PyMem_Free)> path{
315
+ PyUnicode_AsWideCharString(obj, nullptr), &PyMem_Free};
316
+
317
+ if (!path)
318
+ {
319
+ return nullptr;
320
+ }
321
+
322
+ auto cookie = AddDllDirectory(path.get());
323
+
324
+ if (!cookie)
325
+ {
326
+ PyErr_SetFromWindowsErr(GetLastError());
327
+ return nullptr;
328
+ }
329
+
330
+ return PyLong_FromVoidPtr(cookie);
331
+ }
332
+
333
+ static PyObject* remove_dll_directory(PyObject* /*unused*/, PyObject* obj) noexcept
334
+ {
335
+ auto cookie = PyLong_AsVoidPtr(obj);
336
+ if (!cookie && PyErr_Occurred())
337
+ {
338
+ return nullptr;
339
+ }
340
+
341
+ if (!RemoveDllDirectory(cookie))
342
+ {
343
+ PyErr_SetFromWindowsErr(GetLastError());
344
+ return nullptr;
345
+ }
346
+
347
+ Py_RETURN_NONE;
348
+ }
349
+
350
+ /**
351
+ * Equivalent to functools.cache(functools.partial(uuid.UUID, None))
352
+ *
353
+ * This is a performance optimization since the UUID constructor is expensive.
354
+ */
355
+ static PyObject* wrap_uuid_constructor() noexcept
356
+ {
357
+ pyobj_handle uuid_module{PyImport_ImportModule("uuid")};
358
+ if (!uuid_module)
359
+ {
360
+ return nullptr;
361
+ }
362
+
363
+ pyobj_handle uuid_type{PyObject_GetAttrString(uuid_module.get(), "UUID")};
364
+ if (!uuid_type)
365
+ {
366
+ return nullptr;
367
+ }
368
+
369
+ pyobj_handle functools_module{PyImport_ImportModule("functools")};
370
+ if (!functools_module)
371
+ {
372
+ return nullptr;
373
+ }
374
+
375
+ pyobj_handle partial_func{
376
+ PyObject_GetAttrString(functools_module.get(), "partial")};
377
+ if (!partial_func)
378
+ {
379
+ return nullptr;
380
+ }
381
+
382
+ pyobj_handle partial_uuid_func{PyObject_CallFunctionObjArgs(
383
+ partial_func.get(), uuid_type.get(), Py_None, nullptr)};
384
+ if (!partial_uuid_func)
385
+ {
386
+ return nullptr;
387
+ }
388
+
389
+ // REVISIT: we could probably implement a cache function in C++ that
390
+ // would even more performant
391
+ pyobj_handle cache_func{
392
+ PyObject_GetAttrString(functools_module.get(), "cache")};
393
+ if (!cache_func)
394
+ {
395
+ return nullptr;
396
+ }
397
+
398
+ return PyObject_CallOneArg(cache_func.get(), partial_uuid_func.get());
399
+ }
400
+
401
+ static int module_traverse(PyObject* module, visitproc visit, void* arg) noexcept
402
+ {
403
+ auto state = reinterpret_cast<module_state*>(PyModule_GetState(module));
404
+
405
+ Py_VISIT(state->object_type);
406
+ Py_VISIT(state->array_type);
407
+ Py_VISIT(state->mapping_iter_type);
408
+ Py_VISIT(state->to_uuid_func);
409
+
410
+ for (const auto& [key, value] : state->type_cache)
411
+ {
412
+ Py_VISIT(value);
413
+ }
414
+
415
+ return 0;
416
+ }
417
+
418
+ static int module_clear(PyObject* module) noexcept
419
+ {
420
+ auto state = reinterpret_cast<module_state*>(PyModule_GetState(module));
421
+
422
+ Py_CLEAR(state->object_type);
423
+ Py_CLEAR(state->array_type);
424
+ Py_CLEAR(state->mapping_iter_type);
425
+ Py_CLEAR(state->to_uuid_func);
426
+
427
+ auto type_cache = std::move(state->type_cache);
428
+
429
+ for (auto& [key, value] : type_cache)
430
+ {
431
+ Py_XDECREF(value);
432
+ }
433
+
434
+ return 0;
435
+ }
436
+
437
+ static void module_free(PyObject* module) noexcept
438
+ {
439
+ auto state = reinterpret_cast<module_state*>(PyModule_GetState(module));
440
+
441
+ Py_XDECREF(state->object_type);
442
+ Py_XDECREF(state->array_type);
443
+ Py_XDECREF(state->mapping_iter_type);
444
+ Py_XDECREF(state->to_uuid_func);
445
+
446
+ for (auto& [key, value] : state->type_cache)
447
+ {
448
+ Py_XDECREF(value);
449
+ }
450
+
451
+ std::destroy_at(&state->type_cache);
452
+ }
453
+
294
454
  PyDoc_STRVAR(module_doc, "_winrt");
295
455
 
296
456
  static PyMethodDef module_methods[]{
@@ -298,23 +458,33 @@ namespace py::cpp::_winrt
298
458
  {"uninit_apartment",
299
459
  uninit_apartment,
300
460
  METH_NOARGS,
301
- "uninitialize the apartment"},
461
+ PyDoc_STR("uninitialize the apartment")},
302
462
  {"initialize_with_window",
303
463
  initialize_with_window,
304
464
  METH_VARARGS,
305
- "interop function to invoke IInitializeWithWindow::Initialize on an object"},
465
+ PyDoc_STR(
466
+ "interop function to invoke IInitializeWithWindow::Initialize on an object")},
467
+ {"_add_dll_directory",
468
+ add_dll_directory,
469
+ METH_O,
470
+ PyDoc_STR("Adds a directory to the DLL search path.")},
471
+ {"_remove_dll_directory",
472
+ remove_dll_directory,
473
+ METH_O,
474
+ PyDoc_STR(
475
+ "Removes a directory that was added to the process DLL search path by using _add_dll_directory.")},
306
476
  {}};
307
477
 
308
478
  static PyModuleDef module_def
309
479
  = {PyModuleDef_HEAD_INIT,
310
480
  "_winrt",
311
481
  module_doc,
312
- 0,
482
+ sizeof(module_state),
313
483
  module_methods,
314
484
  nullptr,
315
- nullptr,
316
- nullptr,
317
- nullptr};
485
+ module_traverse,
486
+ module_clear,
487
+ reinterpret_cast<freefunc>(module_free)};
318
488
 
319
489
  static PyObject* module_init() noexcept
320
490
  {
@@ -324,27 +494,31 @@ namespace py::cpp::_winrt
324
494
  = static_cast<long>(winrt::apartment_type::single_threaded);
325
495
 
326
496
  py::pyobj_handle module{PyModule_Create(&module_def)};
327
-
328
497
  if (!module)
329
498
  {
330
499
  return nullptr;
331
500
  }
332
501
 
333
- if (py::register_python_type(module.get(), &Object_type_spec, nullptr, nullptr)
334
- == -1)
502
+ auto state = reinterpret_cast<module_state*>(PyModule_GetState(module.get()));
503
+ std::construct_at(&state->type_cache);
504
+
505
+ py::pytype_handle object_type{py::register_python_type(
506
+ module.get(), &Object_type_spec, nullptr, nullptr)};
507
+ if (!object_type)
335
508
  {
336
509
  return nullptr;
337
510
  }
338
511
 
339
- if (py::register_python_type(module.get(), &Array_type_spec, nullptr, nullptr)
340
- == -1)
512
+ py::pytype_handle array_type{
513
+ py::register_python_type(module.get(), &Array_type_spec, nullptr, nullptr)};
514
+ if (!array_type)
341
515
  {
342
516
  return nullptr;
343
517
  }
344
518
 
345
- if (py::register_python_type(
346
- module.get(), &MappingIter_type_spec, nullptr, nullptr)
347
- == -1)
519
+ py::pytype_handle mapping_iter_type{py::register_python_type(
520
+ module.get(), &MappingIter_type_spec, nullptr, nullptr)};
521
+ if (!mapping_iter_type)
348
522
  {
349
523
  return nullptr;
350
524
  }
@@ -375,6 +549,17 @@ namespace py::cpp::_winrt
375
549
  return nullptr;
376
550
  }
377
551
 
552
+ pyobj_handle to_uuid_func{wrap_uuid_constructor()};
553
+ if (!to_uuid_func)
554
+ {
555
+ return nullptr;
556
+ }
557
+
558
+ state->object_type = object_type.detach();
559
+ state->array_type = array_type.detach();
560
+ state->mapping_iter_type = mapping_iter_type.detach();
561
+ state->to_uuid_func = to_uuid_func.detach();
562
+
378
563
  return module.detach();
379
564
  }
380
565
  } // namespace py::cpp::_winrt
@@ -384,35 +569,24 @@ PyMODINIT_FUNC PyInit__winrt(void) noexcept
384
569
  return py::cpp::_winrt::module_init();
385
570
  }
386
571
 
387
- PyTypeObject* py::get_object_type() noexcept
572
+ py::cpp::_winrt::module_state* py::cpp::_winrt::get_module_state() noexcept
388
573
  {
389
- py::pyobj_handle system_module{PyImport_ImportModule("winrt.system")};
390
-
391
- if (!system_module)
574
+ auto module = PyState_FindModule(&py::cpp::_winrt::module_def);
575
+ if (!module)
392
576
  {
393
577
  return nullptr;
394
578
  }
395
579
 
396
- py::pyobj_handle object_type{PyObject_GetAttrString(system_module.get(), "Object")};
397
-
398
- if (!object_type)
399
- {
400
- return nullptr;
401
- }
402
-
403
- if (!PyType_Check(object_type.get()))
404
- {
405
- PyErr_SetString(PyExc_TypeError, "winrt.system.Object is not a type object!");
406
- return nullptr;
407
- }
580
+ return reinterpret_cast<py::cpp::_winrt::module_state*>(PyModule_GetState(module));
581
+ }
408
582
 
409
- if (reinterpret_cast<PyTypeObject*>(object_type.get())->tp_new
410
- != py::cpp::_winrt::Object_new)
583
+ PyTypeObject* py::get_object_type() noexcept
584
+ {
585
+ auto state = py::cpp::_winrt::get_module_state();
586
+ if (!state)
411
587
  {
412
- PyErr_SetString(
413
- PyExc_TypeError, "winrt.system.Object is not the expected type!");
414
588
  return nullptr;
415
589
  }
416
590
 
417
- return reinterpret_cast<PyTypeObject*>(object_type.get());
591
+ return state->object_type;
418
592
  }
@@ -1,5 +1,6 @@
1
1
  #define PYWINRT_RUNTIME_MODULE
2
2
  #include "pybase.h"
3
+ #include "pyruntime.h"
3
4
  #include "py.Windows.Foundation.h"
4
5
  #include <winrt/base.h>
5
6
 
@@ -538,36 +539,13 @@ namespace py::cpp::_winrt
538
539
 
539
540
  static PyTypeObject* get_array_type() noexcept
540
541
  {
541
- py::pyobj_handle system_module{PyImport_ImportModule("winrt.system")};
542
-
543
- if (!system_module)
544
- {
545
- return nullptr;
546
- }
547
-
548
- py::pyobj_handle array_type{
549
- PyObject_GetAttrString(system_module.get(), "Array")};
550
-
551
- if (!array_type)
552
- {
553
- return nullptr;
554
- }
555
-
556
- if (!PyType_Check(array_type.get()))
557
- {
558
- PyErr_SetString(
559
- PyExc_TypeError, "winrt.system.Array is not a type object!");
560
- return nullptr;
561
- }
562
-
563
- if (reinterpret_cast<PyTypeObject*>(array_type.get())->tp_new != Array_tp_new)
542
+ auto state = py::cpp::_winrt::get_module_state();
543
+ if (!state)
564
544
  {
565
- PyErr_SetString(
566
- PyExc_TypeError, "winrt.system.Array is not the expected type!");
567
545
  return nullptr;
568
546
  }
569
547
 
570
- return reinterpret_cast<PyTypeObject*>(array_type.get());
548
+ return state->array_type;
571
549
  }
572
550
 
573
551
  } // namespace py::cpp::_winrt
@@ -16,7 +16,7 @@ classifiers = [
16
16
  "Intended Audience :: Developers",
17
17
  ]
18
18
  dynamic = ["version"]
19
- requires-python = ">=3.9,<3.13"
19
+ requires-python = ">=3.9,<3.14"
20
20
 
21
21
  [project.urls]
22
22
  # Homepage = "https://github.com/pywinrt/pywinrt"
@@ -37,7 +37,8 @@ where = ["src"]
37
37
  # use local winrt-sdk build dependency
38
38
  environment = { PYTHONPATH="../winrt-sdk/src" }
39
39
  # don't install winrt-sdk from PyPI
40
- build-frontend = { name = "build", args = ["--skip-dependency-check", "--no-isolation"] }
40
+ build-frontend = { name = "build[uv]", args = ["--skip-dependency-check", "--no-isolation"] }
41
+ before-build = "uv pip install setuptools"
41
42
  # don't build for PyPy
42
43
  skip = "pp*"
43
44
  # suppress warnings about ARM64 testing
@@ -45,4 +46,4 @@ test-skip = "*-win_arm64"
45
46
 
46
47
  [tool.cibuildwheel.windows]
47
48
  archs = ["x86", "AMD64", "ARM64"]
48
- repair-wheel-command = "python scripts/add_dlls.py {wheel} {dest_dir}"
49
+ repair-wheel-command = "python scripts/add_msvcp140_dll.py {wheel} {dest_dir}"
@@ -0,0 +1 @@
1
+ 2.2.0
@@ -6,6 +6,7 @@
6
6
 
7
7
  #define PYWINRT_RUNTIME_MODULE
8
8
  #include "pybase.h"
9
+ #include "pyruntime.h"
9
10
 
10
11
  // "backport" of Python 3.12 function.
11
12
  #if PY_VERSION_HEX < 0x030C0000
@@ -168,30 +169,93 @@ static PyObject* PyType_FromMetaclass(
168
169
  * @param base_type The base type, a tuple of base types or nullptr to use the base
169
170
  * slot.
170
171
  * @param metaclass Optional metaclass for the type.
171
- * @returns 0 on success or -1 on error.
172
+ * @returns New reference on success or nullptr on error.
172
173
  */
173
174
 
174
- int py::register_python_type(
175
+ PyTypeObject* py::register_python_type(
175
176
  PyObject* module,
176
177
  PyType_Spec* type_spec,
177
178
  PyObject* base_type,
178
179
  PyTypeObject* metaclass) noexcept
179
180
  {
180
- py::pyobj_handle type_object{
181
- PyType_FromMetaclass(metaclass, module, type_spec, base_type)};
181
+ py::pytype_handle type_object{reinterpret_cast<PyTypeObject*>(
182
+ PyType_FromMetaclass(metaclass, module, type_spec, base_type))};
182
183
 
183
184
  if (!type_object)
184
185
  {
185
- return -1;
186
+ return nullptr;
187
+ }
188
+
189
+ if (PyModule_AddType(module, type_object.get()) == -1)
190
+ {
191
+ return nullptr;
192
+ }
193
+
194
+ return type_object.detach();
195
+ }
196
+
197
+ PyTypeObject* py::get_python_type(std::string_view qualified_name) noexcept
198
+ {
199
+ auto state = py::cpp::_winrt::get_module_state();
200
+ if (!state)
201
+ {
202
+ return nullptr;
203
+ }
204
+
205
+ auto it = state->type_cache.find(qualified_name);
206
+ if (it != state->type_cache.end())
207
+ {
208
+ return it->second;
209
+ }
210
+
211
+ auto pos = qualified_name.find_last_of('.');
212
+ if (pos == std::string_view::npos)
213
+ {
214
+ PyErr_Format(
215
+ PyExc_ValueError, "invalid qualified name: %s", qualified_name.data());
216
+ return nullptr;
217
+ }
218
+
219
+ std::string module_name{qualified_name.substr(0, pos)};
220
+ std::string type_name{qualified_name.substr(pos + 1)};
221
+
222
+ pyobj_handle module{PyImport_ImportModule(module_name.c_str())};
223
+ if (!module)
224
+ {
225
+ return nullptr;
226
+ }
227
+
228
+ pyobj_handle type{PyObject_GetAttrString(module.get(), type_name.c_str())};
229
+ if (!type)
230
+ {
231
+ return nullptr;
186
232
  }
187
233
 
188
- if (PyModule_AddType(module, reinterpret_cast<PyTypeObject*>(type_object.get()))
189
- == -1)
234
+ if (!PyType_Check(type.get()))
235
+ {
236
+ PyErr_Format(
237
+ PyExc_TypeError,
238
+ "'%s.%s' is not a type",
239
+ module_name.c_str(),
240
+ type_name.c_str());
241
+ return nullptr;
242
+ }
243
+
244
+ // TODO: verify that the Python type is compatible with the winrt
245
+ // runtime module version and that it actually matches the C++ type
246
+
247
+ try
248
+ {
249
+ state->type_cache[qualified_name]
250
+ = reinterpret_cast<PyTypeObject*>(Py_NewRef(type.get()));
251
+ }
252
+ catch (...)
190
253
  {
191
- return -1;
254
+ to_PyErr();
255
+ return nullptr;
192
256
  }
193
257
 
194
- return 0;
258
+ return reinterpret_cast<PyTypeObject*>(type.get());
195
259
  }
196
260
 
197
261
  /**
@@ -202,22 +266,14 @@ int py::register_python_type(
202
266
  */
203
267
  PyObject* py::wrap_mapping_iter(PyObject* iter) noexcept
204
268
  {
205
- py::pyobj_handle winrt_module{PyImport_ImportModule("winrt._winrt")};
206
-
207
- if (!winrt_module)
208
- {
209
- return nullptr;
210
- }
211
-
212
- py::pyobj_handle mapping_iter_type{
213
- PyObject_GetAttrString(winrt_module.get(), "MappingIter")};
214
-
215
- if (!mapping_iter_type)
269
+ auto state = py::cpp::_winrt::get_module_state();
270
+ if (!state)
216
271
  {
217
272
  return nullptr;
218
273
  }
219
274
 
220
- py::pyobj_handle wrapper{PyObject_CallOneArg(mapping_iter_type.get(), iter)};
275
+ py::pyobj_handle wrapper{PyObject_CallOneArg(
276
+ reinterpret_cast<PyObject*>(state->mapping_iter_type), iter)};
221
277
 
222
278
  if (!wrapper)
223
279
  {
@@ -393,3 +449,62 @@ winrt::Windows::Foundation::DateTime py::convert_to_datetime(PyObject* obj)
393
449
 
394
450
  return value;
395
451
  }
452
+
453
+ PyObject* py::convert_guid(winrt::guid value) noexcept
454
+ {
455
+ auto state = py::cpp::_winrt::get_module_state();
456
+ if (!state)
457
+ {
458
+ return nullptr;
459
+ }
460
+
461
+ // It is faster to swap in C++ rather than using bytes_le in Python since
462
+ // Python stores the bytes in a different order.
463
+ alignas(4) char buffer[sizeof(winrt::guid)];
464
+ *reinterpret_cast<uint32_t*>(buffer) = _byteswap_ulong(value.Data1);
465
+ *reinterpret_cast<uint16_t*>(buffer + 4) = _byteswap_ushort(value.Data2);
466
+ *reinterpret_cast<uint16_t*>(buffer + 6) = _byteswap_ushort(value.Data3);
467
+ std::memcpy(buffer + 8, value.Data4, sizeof(value.Data4));
468
+
469
+ pyobj_handle value_as_bytes{PyBytes_FromStringAndSize(buffer, sizeof(buffer))};
470
+ if (!value_as_bytes)
471
+ {
472
+ return nullptr;
473
+ }
474
+
475
+ return PyObject_CallOneArg(state->to_uuid_func, value_as_bytes.get());
476
+ }
477
+
478
+ winrt::guid py::convert_to_guid(PyObject* obj)
479
+ {
480
+ throw_if_pyobj_null(obj);
481
+
482
+ pyobj_handle bytes{PyObject_GetAttrString(obj, "bytes")};
483
+ if (!bytes)
484
+ {
485
+ throw python_exception();
486
+ }
487
+
488
+ char* buffer;
489
+ Py_ssize_t size;
490
+ if (PyBytes_AsStringAndSize(bytes.get(), &buffer, &size) == -1)
491
+ {
492
+ throw python_exception();
493
+ }
494
+
495
+ if (size != sizeof(winrt::guid))
496
+ {
497
+ PyErr_SetString(PyExc_ValueError, "bytes is wrong size");
498
+ throw python_exception();
499
+ }
500
+
501
+ // It is faster to swap in C++ rather than using bytes_le in Python since
502
+ // Python stores the bytes in a different order.
503
+ winrt::guid result;
504
+ result.Data1 = _byteswap_ulong(*reinterpret_cast<uint32_t*>(buffer));
505
+ result.Data2 = _byteswap_ushort(*reinterpret_cast<uint16_t*>(buffer + 4));
506
+ result.Data3 = _byteswap_ushort(*reinterpret_cast<uint16_t*>(buffer + 6));
507
+ std::memcpy(result.Data4, buffer + 8, sizeof(result.Data4));
508
+
509
+ return result;
510
+ }
@@ -24,6 +24,8 @@ STA: int
24
24
  def init_apartment(apartment_type: int) -> None: ...
25
25
  def uninit_apartment() -> None: ...
26
26
  def initialize_with_window(obj: Object, hwnd: int) -> None: ...
27
+ def _add_dll_directory(path: str) -> int: ...
28
+ def _remove_dll_directory(cookie: int) -> None: ...
27
29
 
28
30
  _Self = TypeVar("_Self")
29
31
  _T = TypeVar("_T")
@@ -1,8 +1,38 @@
1
1
  from collections.abc import Mapping, MutableMapping, MutableSequence, Sequence
2
+ from pathlib import Path
2
3
  from typing import Annotated
3
4
 
4
5
  from .. import _winrt
5
6
 
7
+
8
+ class _DllCookie:
9
+ def __init__(self, cookie: int) -> None:
10
+ self.cookie = cookie
11
+
12
+ def close(self):
13
+ if self.cookie:
14
+ _winrt._remove_dll_directory(self.cookie)
15
+ self.cookie = None
16
+
17
+ def __del__(self):
18
+ self.close()
19
+
20
+
21
+ def _register_dll_search_path(module_path) -> _DllCookie:
22
+ """
23
+ Register a module's directory as a DLL search path.
24
+
25
+ Args:
26
+ module_path: The path to a module file (i.e. ``__file__``)
27
+
28
+ Returns:
29
+ An cookie object that will remove the search path when closed.
30
+ """
31
+ return _DllCookie(
32
+ _winrt._add_dll_directory(str(Path(module_path).parent.resolve()))
33
+ )
34
+
35
+
6
36
  # NB: The types implemented in C cannot inherit from abc.ABC since Python 3.12
7
37
  # so we have to implement the protocols like this instead.
8
38
  # https://github.com/python/cpython/issues/103968#issuecomment-1589928055
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: winrt-runtime
3
- Version: 2.1.0
3
+ Version: 2.2.0
4
4
  Summary: Python projection of Windows Runtime (WinRT) APIs
5
5
  License: MIT
6
6
  Project-URL: Documentation, https://pywinrt.readthedocs.io
@@ -10,7 +10,7 @@ Classifier: Operating System :: Microsoft :: Windows
10
10
  Classifier: Programming Language :: Python :: 3
11
11
  Classifier: Programming Language :: Python :: Implementation :: CPython
12
12
  Classifier: Intended Audience :: Developers
13
- Requires-Python: <3.13,>=3.9
13
+ Requires-Python: <3.14,>=3.9
14
14
  Description-Content-Type: text/markdown
15
15
 
16
16
  # winrt-runtime
@@ -1 +0,0 @@
1
- 2.1.0
File without changes
File without changes
File without changes