winrt-runtime 2.0.1__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.0.1
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
@@ -0,0 +1,592 @@
1
+ // must be included before winrt to avoid compile errors
2
+ #include <Shobjidl.h>
3
+ #include <libloaderapi.h>
4
+
5
+ #define PYWINRT_RUNTIME_MODULE
6
+ #include "pybase.h"
7
+ #include "pyruntime.h"
8
+ #include <winrt/base.h>
9
+
10
+ namespace py::cpp::_winrt
11
+ {
12
+ // BEGIN: class _winrt.Object:
13
+
14
+ static constexpr const char* const type_name_Object = "Object";
15
+
16
+ PyDoc_STRVAR(Object_doc, "base class for wrapped WinRT object instances.");
17
+
18
+ static PyObject* Object_new(
19
+ PyTypeObject* /*unused*/, PyObject* /*unused*/, PyObject* /*unused*/) noexcept
20
+ {
21
+ py::set_invalid_activation_error(type_name_Object);
22
+ return nullptr;
23
+ }
24
+
25
+ static void Object_dealloc(
26
+ py::winrt_wrapper<winrt::Windows::Foundation::IInspectable>* self) noexcept
27
+ {
28
+ auto tp = Py_TYPE(self);
29
+
30
+ std::destroy_at(&self->obj);
31
+ tp->tp_free(self);
32
+ Py_DECREF(tp);
33
+ }
34
+
35
+ PyDoc_STRVAR(
36
+ Object_iids_doc,
37
+ "Gets the interfaces that are implemented by the current Windows Runtime class.");
38
+
39
+ static PyObject* Object_iids_get(
40
+ py::winrt_wrapper<winrt::Windows::Foundation::IInspectable>* self,
41
+ void* /*unused*/) noexcept
42
+ {
43
+ try
44
+ {
45
+ auto name = winrt::get_interfaces(self->obj);
46
+ return convert(name);
47
+ }
48
+ catch (...)
49
+ {
50
+ py::to_PyErr();
51
+ return nullptr;
52
+ }
53
+ }
54
+
55
+ PyDoc_STRVAR(
56
+ Object_runtime_class_name_doc,
57
+ "Gets the fully qualified name of the current Windows Runtime object.");
58
+
59
+ static PyObject* Object_runtime_class_name_get(
60
+ py::winrt_wrapper<winrt::Windows::Foundation::IInspectable>* self,
61
+ void* /*unused*/) noexcept
62
+ {
63
+ try
64
+ {
65
+ auto name = winrt::get_class_name(self->obj);
66
+ return convert(name);
67
+ }
68
+ catch (...)
69
+ {
70
+ py::to_PyErr();
71
+ return nullptr;
72
+ }
73
+ }
74
+
75
+ static PyGetSetDef Object_getset[]
76
+ = {{"_iids_",
77
+ reinterpret_cast<getter>(Object_iids_get),
78
+ nullptr,
79
+ Object_iids_doc,
80
+ nullptr},
81
+ {"_runtime_class_name_",
82
+ reinterpret_cast<getter>(Object_runtime_class_name_get),
83
+ nullptr,
84
+ Object_runtime_class_name_doc,
85
+ nullptr},
86
+ {}};
87
+
88
+ static PyType_Slot Object_type_slots[]
89
+ = {{Py_tp_doc, const_cast<char*>(Object_doc)},
90
+ {Py_tp_new, reinterpret_cast<void*>(Object_new)},
91
+ {Py_tp_dealloc, reinterpret_cast<void*>(Object_dealloc)},
92
+ {Py_tp_getset, reinterpret_cast<void*>(Object_getset)},
93
+ {}};
94
+
95
+ static PyType_Spec Object_type_spec
96
+ = {"_winrt.Object",
97
+ sizeof(py::winrt_wrapper<winrt::Windows::Foundation::IUnknown>),
98
+ 0,
99
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
100
+ Object_type_slots};
101
+
102
+ // END: class _winrt.Object:
103
+
104
+ // BEGIN: class _winrt.Array:
105
+
106
+ extern PyType_Spec Array_type_spec;
107
+
108
+ // END: class _winrt.Array:
109
+
110
+ // BEGIN: class _winrt.MappingIter:
111
+
112
+ // This class is used to wrap the iterator returned by IMap/IMapView so
113
+ // that it returns only the key instead of a KeyValuePair. This is done
114
+ // to be consistent with the Python mapping protocol.
115
+ //
116
+ // In Python it would look something like this:
117
+ //
118
+ // class MappingIter:
119
+ // def __init__(self, base_iter):
120
+ // self._iter = iter(base_iter)
121
+ //
122
+ // def __iter__(self):
123
+ // return self
124
+ //
125
+ // def __next__(self):
126
+ // return next(self._iter).key
127
+ //
128
+
129
+ struct MappingIter_object
130
+ {
131
+ PyObject_HEAD;
132
+ PyObject* _iter;
133
+ PyObject* _key;
134
+ };
135
+
136
+ static PyMemberDef MappingIter_members[]
137
+ = {{"_iter",
138
+ T_OBJECT_EX,
139
+ offsetof(MappingIter_object, _iter),
140
+ 0,
141
+ PyDoc_STR("base KeyValuePair iterator")},
142
+ {"_key", T_OBJECT, offsetof(MappingIter_object, _key), 0, nullptr},
143
+ {}};
144
+
145
+ static int MappingIter_init(PyObject* self, PyObject* args, PyObject* kwds) noexcept
146
+ {
147
+ if (kwds)
148
+ {
149
+ PyErr_SetString(PyExc_TypeError, "keyword arguments are not supported");
150
+ return -1;
151
+ }
152
+
153
+ // borrowed ref
154
+ auto base_iter = PyTuple_GetItem(args, 0);
155
+
156
+ if (!base_iter)
157
+ {
158
+ return -1;
159
+ }
160
+
161
+ if (!PyIter_Check(base_iter))
162
+ {
163
+ PyErr_SetString(PyExc_TypeError, "expecting an iterator");
164
+ return -1;
165
+ }
166
+
167
+ if (PyObject_SetAttrString(self, "_iter", base_iter) == -1)
168
+ {
169
+ return -1;
170
+ }
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
+
183
+ return 0;
184
+ }
185
+
186
+ static PyObject* MappingIter_iternext(MappingIter_object* self) noexcept
187
+ {
188
+ if (!self->_iter)
189
+ {
190
+ PyErr_SetString(PyExc_RuntimeError, "_iter was deleted");
191
+ return nullptr;
192
+ }
193
+
194
+ py::pyobj_handle next{PyIter_Next(self->_iter)};
195
+ if (!next)
196
+ {
197
+ return nullptr;
198
+ }
199
+
200
+ if (!self->_key)
201
+ {
202
+ PyErr_SetString(PyExc_RuntimeError, "_key was deleted");
203
+ return nullptr;
204
+ }
205
+
206
+ py::pyobj_handle key{PyObject_GetAttr(next.get(), self->_key)};
207
+ if (!key)
208
+ {
209
+ return nullptr;
210
+ }
211
+
212
+ return key.detach();
213
+ }
214
+
215
+ PyDoc_STRVAR(MappingIter_doc, "Utility class for wrapping KeyValuePair iterators.");
216
+
217
+ static PyType_Slot MappingIter_type_slots[] = {
218
+ {Py_tp_members, reinterpret_cast<void*>(MappingIter_members)},
219
+ {Py_tp_init, reinterpret_cast<void*>(MappingIter_init)},
220
+ {Py_tp_iter, reinterpret_cast<void*>(PyObject_SelfIter)},
221
+ {Py_tp_iternext, reinterpret_cast<void*>(MappingIter_iternext)},
222
+ {Py_tp_doc, const_cast<char*>(MappingIter_doc)},
223
+ {},
224
+ };
225
+
226
+ static PyType_Spec MappingIter_type_spec
227
+ = {"_winrt.MappingIter",
228
+ sizeof(MappingIter_object),
229
+ 0,
230
+ Py_TPFLAGS_DEFAULT,
231
+ MappingIter_type_slots};
232
+
233
+ // END: class _winrt.MappingIter:
234
+
235
+ static const py::runtime_api runtime_api{
236
+ .runtime_api_guid = py::runtime_api_guid,
237
+ .abi_version_major = py::runtime_abi_version_major,
238
+ .abi_version_minor = py::runtime_abi_version_minor,
239
+ .register_python_type = py::register_python_type,
240
+ .get_python_type = py::get_python_type,
241
+ .wrap_mapping_iter = py::wrap_mapping_iter,
242
+ .is_buffer_compatible = py::is_buffer_compatible,
243
+ .convert_datetime = py::convert_datetime,
244
+ .convert_to_datetime = py::convert_to_datetime,
245
+ .convert_guid = py::convert_guid,
246
+ .convert_to_guid = py::convert_to_guid,
247
+ .get_object_type = py::get_object_type,
248
+ .array_new = py::cpp::_winrt::Array_New,
249
+ .array_assign = &py::cpp::_winrt::Array_Assign,
250
+ };
251
+
252
+ static PyObject* init_apartment(PyObject* /*unused*/, PyObject* type_obj) noexcept
253
+ {
254
+ auto type = PyLong_AsLong(type_obj);
255
+
256
+ if (type == -1 && PyErr_Occurred())
257
+ {
258
+ return nullptr;
259
+ }
260
+
261
+ try
262
+ {
263
+ winrt::init_apartment(static_cast<winrt::apartment_type>(type));
264
+ Py_RETURN_NONE;
265
+ }
266
+ catch (...)
267
+ {
268
+ py::to_PyErr();
269
+ return nullptr;
270
+ }
271
+ }
272
+
273
+ static PyObject* uninit_apartment(
274
+ PyObject* /*unused*/, PyObject* /*unused*/) noexcept
275
+ {
276
+ winrt::uninit_apartment();
277
+ Py_RETURN_NONE;
278
+ }
279
+
280
+ static PyObject* initialize_with_window(
281
+ PyObject* /*unused*/, PyObject* args) noexcept
282
+ {
283
+ PyObject* obj;
284
+ Py_ssize_t hwnd;
285
+
286
+ if (!PyArg_ParseTuple(args, "On", &obj, &hwnd))
287
+ {
288
+ return nullptr;
289
+ }
290
+
291
+ try
292
+ {
293
+ auto winrt_obj
294
+ = py::convert_to<winrt::Windows::Foundation::IInspectable>(obj);
295
+ auto result = winrt_obj.as<IInitializeWithWindow>()->Initialize(
296
+ reinterpret_cast<HWND>(hwnd));
297
+
298
+ if (result != S_OK)
299
+ {
300
+ winrt::throw_hresult(result);
301
+ }
302
+ }
303
+ catch (...)
304
+ {
305
+ py::to_PyErr();
306
+ return nullptr;
307
+ }
308
+
309
+ Py_RETURN_NONE;
310
+ }
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
+
454
+ PyDoc_STRVAR(module_doc, "_winrt");
455
+
456
+ static PyMethodDef module_methods[]{
457
+ {"init_apartment", init_apartment, METH_O, "initialize the apartment"},
458
+ {"uninit_apartment",
459
+ uninit_apartment,
460
+ METH_NOARGS,
461
+ PyDoc_STR("uninitialize the apartment")},
462
+ {"initialize_with_window",
463
+ initialize_with_window,
464
+ METH_VARARGS,
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.")},
476
+ {}};
477
+
478
+ static PyModuleDef module_def
479
+ = {PyModuleDef_HEAD_INIT,
480
+ "_winrt",
481
+ module_doc,
482
+ sizeof(module_state),
483
+ module_methods,
484
+ nullptr,
485
+ module_traverse,
486
+ module_clear,
487
+ reinterpret_cast<freefunc>(module_free)};
488
+
489
+ static PyObject* module_init() noexcept
490
+ {
491
+ static const auto kMTA
492
+ = static_cast<long>(winrt::apartment_type::multi_threaded);
493
+ static const auto kSTA
494
+ = static_cast<long>(winrt::apartment_type::single_threaded);
495
+
496
+ py::pyobj_handle module{PyModule_Create(&module_def)};
497
+ if (!module)
498
+ {
499
+ return nullptr;
500
+ }
501
+
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)
508
+ {
509
+ return nullptr;
510
+ }
511
+
512
+ py::pytype_handle array_type{
513
+ py::register_python_type(module.get(), &Array_type_spec, nullptr, nullptr)};
514
+ if (!array_type)
515
+ {
516
+ return nullptr;
517
+ }
518
+
519
+ py::pytype_handle mapping_iter_type{py::register_python_type(
520
+ module.get(), &MappingIter_type_spec, nullptr, nullptr)};
521
+ if (!mapping_iter_type)
522
+ {
523
+ return nullptr;
524
+ }
525
+
526
+ pyobj_handle runtime_api_capsule{PyCapsule_New(
527
+ const_cast<py::runtime_api*>(&runtime_api),
528
+ "winrt._winrt._C_API",
529
+ nullptr)};
530
+
531
+ if (!runtime_api_capsule)
532
+ {
533
+ return nullptr;
534
+ }
535
+
536
+ if (PyModule_AddObjectRef(module.get(), "_C_API", runtime_api_capsule.get())
537
+ == -1)
538
+ {
539
+ return nullptr;
540
+ }
541
+
542
+ if (PyModule_AddIntConstant(module.get(), "MTA", kMTA) == -1)
543
+ {
544
+ return nullptr;
545
+ }
546
+
547
+ if (PyModule_AddIntConstant(module.get(), "STA", kSTA) == -1)
548
+ {
549
+ return nullptr;
550
+ }
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
+
563
+ return module.detach();
564
+ }
565
+ } // namespace py::cpp::_winrt
566
+
567
+ PyMODINIT_FUNC PyInit__winrt(void) noexcept
568
+ {
569
+ return py::cpp::_winrt::module_init();
570
+ }
571
+
572
+ py::cpp::_winrt::module_state* py::cpp::_winrt::get_module_state() noexcept
573
+ {
574
+ auto module = PyState_FindModule(&py::cpp::_winrt::module_def);
575
+ if (!module)
576
+ {
577
+ return nullptr;
578
+ }
579
+
580
+ return reinterpret_cast<py::cpp::_winrt::module_state*>(PyModule_GetState(module));
581
+ }
582
+
583
+ PyTypeObject* py::get_object_type() noexcept
584
+ {
585
+ auto state = py::cpp::_winrt::get_module_state();
586
+ if (!state)
587
+ {
588
+ return nullptr;
589
+ }
590
+
591
+ return state->object_type;
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
+ }
@@ -16,6 +16,7 @@ from typing import (
16
16
  ValuesView,
17
17
  overload,
18
18
  )
19
+ from uuid import UUID
19
20
 
20
21
  MTA: int
21
22
  STA: int
@@ -23,6 +24,8 @@ STA: int
23
24
  def init_apartment(apartment_type: int) -> None: ...
24
25
  def uninit_apartment() -> None: ...
25
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: ...
26
29
 
27
30
  _Self = TypeVar("_Self")
28
31
  _T = TypeVar("_T")
@@ -82,7 +85,11 @@ class MutableMapping(Mapping[_KT, _VT]):
82
85
 
83
86
  # actual runtime classes
84
87
 
85
- class Object: ...
88
+ class Object:
89
+ @property
90
+ def _iids_(self) -> Array[UUID]: ...
91
+ @property
92
+ def _runtime_class_name_(self) -> str: ...
86
93
 
87
94
  class Array(MutableSequence[_T]):
88
95
  _winrt_element_type_name_: str
@@ -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.0.1
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,365 +0,0 @@
1
- // must be included before winrt to avoid compile errors
2
- #include <Shobjidl.h>
3
-
4
- #define PYWINRT_RUNTIME_MODULE
5
- #include "pybase.h"
6
- #include <winrt/base.h>
7
-
8
- namespace py::cpp::_winrt
9
- {
10
- // BEGIN: class _winrt.Object:
11
-
12
- static constexpr const char* const type_name_Object = "Object";
13
-
14
- PyDoc_STRVAR(Object_doc, "base class for wrapped WinRT object instances.");
15
-
16
- static PyObject* Object_new(
17
- PyTypeObject* /*unused*/, PyObject* /*unused*/, PyObject* /*unused*/) noexcept
18
- {
19
- py::set_invalid_activation_error(type_name_Object);
20
- return nullptr;
21
- }
22
-
23
- static void Object_dealloc(
24
- py::winrt_wrapper<winrt::Windows::Foundation::IInspectable>* self)
25
- {
26
- auto tp = Py_TYPE(self);
27
-
28
- std::destroy_at(&self->obj);
29
- tp->tp_free(self);
30
- Py_DECREF(tp);
31
- }
32
-
33
- static PyType_Slot Object_type_slots[] = {
34
- {Py_tp_new, reinterpret_cast<void*>(Object_new)},
35
- {Py_tp_dealloc, reinterpret_cast<void*>(Object_dealloc)},
36
- {Py_tp_doc, const_cast<char*>(Object_doc)},
37
- {},
38
- };
39
-
40
- static PyType_Spec Object_type_spec
41
- = {"_winrt.Object",
42
- sizeof(py::winrt_wrapper<winrt::Windows::Foundation::IUnknown>),
43
- 0,
44
- Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
45
- Object_type_slots};
46
-
47
- // END: class _winrt.Object:
48
-
49
- // BEGIN: class _winrt.Array:
50
-
51
- extern PyType_Spec Array_type_spec;
52
-
53
- // END: class _winrt.Array:
54
-
55
- // BEGIN: class _winrt.MappingIter:
56
-
57
- // This class is used to wrap the iterator returned by IMap/IMapView so
58
- // that it returns only the key instead of a KeyValuePair. This is done
59
- // to be consistent with the Python mapping protocol.
60
- //
61
- // In Python it would look something like this:
62
- //
63
- // class MappingIter:
64
- // def __init__(self, base_iter):
65
- // self._iter = iter(base_iter)
66
- //
67
- // def __iter__(self):
68
- // return self
69
- //
70
- // def __next__(self):
71
- // return next(self._iter).key
72
- //
73
-
74
- struct MappingIter_object
75
- {
76
- PyObject_HEAD;
77
- PyObject* _iter;
78
- };
79
-
80
- static PyMemberDef MappingIter_members[]
81
- = {{"_iter",
82
- T_OBJECT_EX,
83
- offsetof(MappingIter_object, _iter),
84
- 0,
85
- PyDoc_STR("base KeyValuePair iterator")},
86
- {}};
87
-
88
- static int MappingIter_init(PyObject* self, PyObject* args, PyObject* kwds) noexcept
89
- {
90
- if (kwds)
91
- {
92
- PyErr_SetString(PyExc_TypeError, "keyword arguments are not supported");
93
- return -1;
94
- }
95
-
96
- // borrowed ref
97
- auto base_iter = PyTuple_GetItem(args, 0);
98
-
99
- if (!base_iter)
100
- {
101
- return -1;
102
- }
103
-
104
- if (!PyIter_Check(base_iter))
105
- {
106
- PyErr_SetString(PyExc_TypeError, "expecting an iterator");
107
- return -1;
108
- }
109
-
110
- if (PyObject_SetAttrString(self, "_iter", base_iter) == -1)
111
- {
112
- return -1;
113
- }
114
-
115
- return 0;
116
- }
117
-
118
- static PyObject* MappingIter_iternext(PyObject* self) noexcept
119
- {
120
- // new reference
121
- py::pyobj_handle base_iter{PyObject_GetAttrString(self, "_iter")};
122
-
123
- if (!base_iter)
124
- {
125
- return nullptr;
126
- }
127
-
128
- // new reference
129
- py::pyobj_handle next{PyIter_Next(base_iter.get())};
130
-
131
- if (!next)
132
- {
133
- return nullptr;
134
- }
135
-
136
- // new reference
137
- py::pyobj_handle key{PyObject_GetAttrString(next.get(), "key")};
138
-
139
- if (!key)
140
- {
141
- return nullptr;
142
- }
143
-
144
- return key.detach();
145
- }
146
-
147
- PyDoc_STRVAR(MappingIter_doc, "Utility class for wrapping KeyValuePair iterators.");
148
-
149
- static PyType_Slot MappingIter_type_slots[] = {
150
- {Py_tp_members, reinterpret_cast<void*>(MappingIter_members)},
151
- {Py_tp_init, reinterpret_cast<void*>(MappingIter_init)},
152
- {Py_tp_iter, reinterpret_cast<void*>(PyObject_SelfIter)},
153
- {Py_tp_iternext, reinterpret_cast<void*>(MappingIter_iternext)},
154
- {Py_tp_doc, const_cast<char*>(MappingIter_doc)},
155
- {},
156
- };
157
-
158
- static PyType_Spec MappingIter_type_spec
159
- = {"_winrt.MappingIter",
160
- sizeof(MappingIter_object),
161
- 0,
162
- Py_TPFLAGS_DEFAULT,
163
- MappingIter_type_slots};
164
-
165
- // END: class _winrt.MappingIter:
166
-
167
- static const py::runtime_api runtime_api{
168
- .runtime_api_guid = py::runtime_api_guid,
169
- .abi_version_major = py::runtime_abi_version_major,
170
- .abi_version_minor = py::runtime_abi_version_minor,
171
- .register_python_type = py::register_python_type,
172
- .wrap_mapping_iter = py::wrap_mapping_iter,
173
- .is_buffer_compatible = py::is_buffer_compatible,
174
- .convert_datetime = py::convert_datetime,
175
- .convert_to_datetime = py::convert_to_datetime,
176
- .get_object_type = py::get_object_type,
177
- .array_new = py::cpp::_winrt::Array_New,
178
- .array_assign = &py::cpp::_winrt::Array_Assign,
179
- };
180
-
181
- static PyObject* init_apartment(PyObject* /*unused*/, PyObject* type_obj) noexcept
182
- {
183
- auto type = PyLong_AsLong(type_obj);
184
-
185
- if (type == -1 && PyErr_Occurred())
186
- {
187
- return nullptr;
188
- }
189
-
190
- try
191
- {
192
- winrt::init_apartment(static_cast<winrt::apartment_type>(type));
193
- Py_RETURN_NONE;
194
- }
195
- catch (...)
196
- {
197
- py::to_PyErr();
198
- return nullptr;
199
- }
200
- }
201
-
202
- static PyObject* uninit_apartment(
203
- PyObject* /*unused*/, PyObject* /*unused*/) noexcept
204
- {
205
- winrt::uninit_apartment();
206
- Py_RETURN_NONE;
207
- }
208
-
209
- static PyObject* initialize_with_window(
210
- PyObject* /*unused*/, PyObject* args) noexcept
211
- {
212
- PyObject* obj;
213
- Py_ssize_t hwnd;
214
-
215
- if (!PyArg_ParseTuple(args, "On", &obj, &hwnd))
216
- {
217
- return nullptr;
218
- }
219
-
220
- try
221
- {
222
- auto winrt_obj
223
- = py::convert_to<winrt::Windows::Foundation::IInspectable>(obj);
224
- auto result = winrt_obj.as<IInitializeWithWindow>()->Initialize(
225
- reinterpret_cast<HWND>(hwnd));
226
-
227
- if (result != S_OK)
228
- {
229
- winrt::throw_hresult(result);
230
- }
231
- }
232
- catch (...)
233
- {
234
- py::to_PyErr();
235
- return nullptr;
236
- }
237
-
238
- Py_RETURN_NONE;
239
- }
240
-
241
- PyDoc_STRVAR(module_doc, "_winrt");
242
-
243
- static PyMethodDef module_methods[]{
244
- {"init_apartment", init_apartment, METH_O, "initialize the apartment"},
245
- {"uninit_apartment",
246
- uninit_apartment,
247
- METH_NOARGS,
248
- "uninitialize the apartment"},
249
- {"initialize_with_window",
250
- initialize_with_window,
251
- METH_VARARGS,
252
- "interop function to invoke IInitializeWithWindow::Initialize on an object"},
253
- {}};
254
-
255
- static PyModuleDef module_def
256
- = {PyModuleDef_HEAD_INIT,
257
- "_winrt",
258
- module_doc,
259
- 0,
260
- module_methods,
261
- nullptr,
262
- nullptr,
263
- nullptr,
264
- nullptr};
265
-
266
- static PyObject* module_init() noexcept
267
- {
268
- static const auto kMTA
269
- = static_cast<long>(winrt::apartment_type::multi_threaded);
270
- static const auto kSTA
271
- = static_cast<long>(winrt::apartment_type::single_threaded);
272
-
273
- py::pyobj_handle module{PyModule_Create(&module_def)};
274
-
275
- if (!module)
276
- {
277
- return nullptr;
278
- }
279
-
280
- if (py::register_python_type(module.get(), &Object_type_spec, nullptr, nullptr)
281
- == -1)
282
- {
283
- return nullptr;
284
- }
285
-
286
- if (py::register_python_type(module.get(), &Array_type_spec, nullptr, nullptr)
287
- == -1)
288
- {
289
- return nullptr;
290
- }
291
-
292
- if (py::register_python_type(
293
- module.get(), &MappingIter_type_spec, nullptr, nullptr)
294
- == -1)
295
- {
296
- return nullptr;
297
- }
298
-
299
- pyobj_handle runtime_api_capsule{PyCapsule_New(
300
- const_cast<py::runtime_api*>(&runtime_api),
301
- "winrt._winrt._C_API",
302
- nullptr)};
303
-
304
- if (!runtime_api_capsule)
305
- {
306
- return nullptr;
307
- }
308
-
309
- if (PyModule_AddObjectRef(module.get(), "_C_API", runtime_api_capsule.get())
310
- == -1)
311
- {
312
- return nullptr;
313
- }
314
-
315
- if (PyModule_AddIntConstant(module.get(), "MTA", kMTA) == -1)
316
- {
317
- return nullptr;
318
- }
319
-
320
- if (PyModule_AddIntConstant(module.get(), "STA", kSTA) == -1)
321
- {
322
- return nullptr;
323
- }
324
-
325
- return module.detach();
326
- }
327
- } // namespace py::cpp::_winrt
328
-
329
- PyMODINIT_FUNC PyInit__winrt(void) noexcept
330
- {
331
- return py::cpp::_winrt::module_init();
332
- }
333
-
334
- PyTypeObject* py::get_object_type() noexcept
335
- {
336
- py::pyobj_handle system_module{PyImport_ImportModule("winrt.system")};
337
-
338
- if (!system_module)
339
- {
340
- return nullptr;
341
- }
342
-
343
- py::pyobj_handle object_type{PyObject_GetAttrString(system_module.get(), "Object")};
344
-
345
- if (!object_type)
346
- {
347
- return nullptr;
348
- }
349
-
350
- if (!PyType_Check(object_type.get()))
351
- {
352
- PyErr_SetString(PyExc_TypeError, "winrt.system.Object is not a type object!");
353
- return nullptr;
354
- }
355
-
356
- if (reinterpret_cast<PyTypeObject*>(object_type.get())->tp_new
357
- != py::cpp::_winrt::Object_new)
358
- {
359
- PyErr_SetString(
360
- PyExc_TypeError, "winrt.system.Object is not the expected type!");
361
- return nullptr;
362
- }
363
-
364
- return reinterpret_cast<PyTypeObject*>(object_type.get());
365
- }
@@ -1 +0,0 @@
1
- 2.0.1
File without changes
File without changes
File without changes