From 05308029ce8e6bb6ac6fdf93e09ecd6e2a0d5b9f Mon Sep 17 00:00:00 2001 From: jaimergp Date: Mon, 11 Dec 2023 10:38:25 +0100 Subject: [PATCH] Add support for Python 3.12 (#164) * update CI matrix * install conda with --only-deps for now * state conda deps explicitly instead * use PyObject + PyUnicode_AsWideCharString * use str() explicitly * pass default values? * allow optional values again * better cleanup * these cannot be NULL * amend docstring * guard these frees * more like this part * add tmate * remove Py_DECREF calls for now * remove tmate * update changelog with 1.4.20 details * revert to regular conda dep * revert win32 * add news --- .github/workflows/tests.yml | 9 +++- CHANGELOG.md | 3 ++ news/164-py312 | 19 ++++++++ src/winshortcut.cpp | 91 +++++++++++++++++++++++++++++-------- 4 files changed, 102 insertions(+), 20 deletions(-) create mode 100644 news/164-py312 diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 144917c6..20bf9292 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -33,7 +33,14 @@ jobs: fail-fast: false matrix: os: [windows, ubuntu, macos] - python-version: ["3.8", "3.9", "3.10"] # "3.11"] + python-version: ["3.8", "3.9", "3.10", "3.11", "3.12"] + exclude: + - os: macos + python-version: "3.11" + - os: macos + python-version: "3.10" + - os: macos + python-version: "3.9" steps: - uses: actions/checkout@v3 diff --git a/CHANGELOG.md b/CHANGELOG.md index 56f75179..46a28702 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -39,6 +39,9 @@ * @dbast made their first contribution in https://github.com/conda/menuinst/pull/116 * @aganders3 made their first contribution in https://github.com/conda/menuinst/pull/119 +## 1.4.20 (2023-10-30) + + * Add support for Python 3.12 (#165) ## 1.4.19 (2022-08-17) diff --git a/news/164-py312 b/news/164-py312 new file mode 100644 index 00000000..23b954c9 --- /dev/null +++ b/news/164-py312 @@ -0,0 +1,19 @@ +### Enhancements + +* Update `winshortcut` C extension to support Python 3.12. (#164) + +### Bug fixes + +* + +### Deprecations + +* + +### Docs + +* + +### Other + +* diff --git a/src/winshortcut.cpp b/src/winshortcut.cpp index 0be6e136..c8700af0 100644 --- a/src/winshortcut.cpp +++ b/src/winshortcut.cpp @@ -32,15 +32,18 @@ static PyObject *CreateShortcut(PyObject *self, PyObject *args) { - Py_UNICODE *path; /* path and filename */ - Py_UNICODE *description; - Py_UNICODE *filename; + PyObject *py_path; /* path and filename */ + wchar_t *path = NULL; + PyObject *py_description; + wchar_t *description = NULL; + PyObject *py_filename; + wchar_t *filename = NULL; - Py_UNICODE *arguments = NULL; - Py_UNICODE *iconpath = NULL; + PyObject *py_arguments = NULL; + PyObject *py_iconpath = NULL; int iconindex = 0; - Py_UNICODE *workdir = NULL; - Py_UNICODE *app_id = NULL; + PyObject *py_workdir = NULL; + PyObject *py_app_id = NULL; IShellLink *pShellLink = NULL; IPersistFile *pPersistFile = NULL; @@ -56,10 +59,23 @@ static PyObject *CreateShortcut(PyObject *self, PyObject *args) goto error; } - if (!PyArg_ParseTuple(args, "uuu|uuuiu", - &path, &description, &filename, - &arguments, &workdir, &iconpath, &iconindex, &app_id)) { - return NULL; + if (!PyArg_ParseTuple(args, "UUU|UUUiU", + &py_path, &py_description, &py_filename, + &py_arguments, &py_workdir, &py_iconpath, &iconindex, &py_app_id)) { + goto error; + } + + path = PyUnicode_AsWideCharString(py_path, NULL); + if (path == NULL) { + goto error; + } + description = PyUnicode_AsWideCharString(py_description, NULL); + if (description == NULL) { + goto error; + } + filename = PyUnicode_AsWideCharString(py_filename, NULL); + if (filename == NULL) { + goto error; } hres = CoCreateInstance(CLSID_ShellLink, NULL, @@ -94,34 +110,56 @@ static PyObject *CreateShortcut(PyObject *self, PyObject *args) goto error; } - if (arguments) { + if (py_arguments) { + wchar_t *arguments = PyUnicode_AsWideCharString(py_arguments, NULL); + if (arguments == NULL) { + goto error; + } hres = pShellLink->SetArguments(arguments); if (FAILED(hres)) { PyErr_Format(PyExc_OSError, "SetArguments() error 0x%x", hres); + PyMem_Free(arguments); goto error; } + PyMem_Free(arguments); } - if (iconpath) { + if (py_iconpath) { + wchar_t *iconpath = PyUnicode_AsWideCharString(py_iconpath, NULL); + if (iconpath == NULL) { + goto error; + } hres = pShellLink->SetIconLocation(iconpath, iconindex); if (FAILED(hres)) { PyErr_Format(PyExc_OSError, "SetIconLocation() error 0x%x", hres); + PyMem_Free(iconpath); goto error; } + PyMem_Free(iconpath); } - if (workdir) { + if (py_workdir) { + wchar_t *workdir = PyUnicode_AsWideCharString(py_workdir, NULL); + if (workdir == NULL) { + goto error; + } hres = pShellLink->SetWorkingDirectory(workdir); if (FAILED(hres)) { - PyErr_Format(PyExc_OSError, - "SetWorkingDirectory() error 0x%x", hres); - goto error; + PyErr_Format(PyExc_OSError, + "SetWorkingDirectory() error 0x%x", hres); + PyMem_Free(workdir); + goto error; } + PyMem_Free(workdir); } - if (app_id) { + if (py_app_id) { + wchar_t *app_id = PyUnicode_AsWideCharString(py_app_id, NULL); + if (app_id == NULL) { + goto error; + } hres = pShellLink->QueryInterface(IID_PPV_ARGS(&pPropertyStore)); if (FAILED(hres)) { PyErr_Format(PyExc_OSError, @@ -134,6 +172,7 @@ static PyObject *CreateShortcut(PyObject *self, PyObject *args) "InitPropVariantFromString() error 0x%x", hres); goto error; } + PyMem_Free(app_id); pPropertyStore->SetValue(PKEY_AppUserModel_ID, pv); pPropertyStore->Commit(); PropVariantClear(&pv); @@ -159,6 +198,10 @@ static PyObject *CreateShortcut(PyObject *self, PyObject *args) pPersistFile->Release(); pShellLink->Release(); + PyMem_Free(path); + PyMem_Free(description); + PyMem_Free(filename); + CoUninitialize(); Py_RETURN_NONE; @@ -173,6 +216,16 @@ static PyObject *CreateShortcut(PyObject *self, PyObject *args) pPropertyStore->Release(); } + if (path) { + PyMem_Free(path); + } + if (description) { + PyMem_Free(description); + } + if (filename) { + PyMem_Free(filename); + } + CoUninitialize(); return NULL; } @@ -181,7 +234,7 @@ PyMethodDef meth[] = { {"create_shortcut", CreateShortcut, METH_VARARGS, "winshortcut.create_shortcut(path, description, filename,\n" " arguments=u\"\", workdir=None, iconpath=None,\n" - " iconindex=0)\n" + " iconindex=0, app_id=None)\n" "\n" " Creates a shortcut ``filename`` (a .lnk file), whose\n" " target path is ``path``. All the input strings must be\n"