Skip to content

Commit 1640496

Browse files
committed
Merge branch 'main' of https://github.com/mhammond/pywin32 into pywin32_postinstall-relative-path-support
2 parents 469fc79 + 40128b4 commit 1640496

25 files changed

+138
-110
lines changed

.github/ISSUE_TEMPLATE/config.yml

+3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
blank_issues_enabled: false
22
contact_links:
3+
- name: Typeshed type stubs and annotations
4+
url: https://github.com/python/typeshed/issues?q=state%3Aopen+pywin32
5+
about: For static type-checking and IntelliSense
36
- name: python-win32 mailing list
47
url: http://mail.python.org/mailman/listinfo/python-win32
58
about: For support requests, problems or questions

.github/ISSUE_TEMPLATE/issue_template.md

+4-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,10 @@ about: Do not open github issues for general support requests
44
---
55

66
<!--
7-
Note that issues in this repository are only for bugs or feature requests in the pywin32.
7+
Note that issues in this repository are only for bugs or feature requests in the pywin32 repository.
8+
9+
Type stubs currently live in [typeshed](<https://github.com/python/typeshed/tree/main/stubs/pywin32>).
10+
Any issue or request related to static type-checking and IntelliSense should be raised there.
811
912
**If you need support or help using this package, please follow [these instructions](https://github.com/mhammond/pywin32/blob/main/README.md#support)** - support or help requests will be closed without comment.
1013
-->

.github/workflows/main.yml

+12-5
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,14 @@ jobs:
3939
pip --version
4040
pip install --upgrade setuptools>=74 wheel
4141
42+
- name: Fix user Scripts missing from PATH
43+
if: matrix.architecture == 'x86'
44+
run: |
45+
# Work around https://github.com/actions/setup-python/issues/1005
46+
$ScriptsPath = python -c "import sysconfig,os; print(sysconfig.get_path('scripts', f'{os.name}_user'))"
47+
echo $ScriptsPath
48+
Add-Content $env:GITHUB_PATH $ScriptsPath
49+
4250
- name: Build and install
4351
run: pip install . -v --user
4452

@@ -49,18 +57,17 @@ jobs:
4957
- name: Generate PyWin32.chm help file
5058
run: python AutoDuck/make.py
5159

52-
# Smokescreen test to validate it doesn't crash and dlls can be found
60+
# Smokescreen test to validate postinstall doesn't crash, dlls can be found, and both pathless invocation methods work
5361
- name: Run postinstall install/remove
5462
run: |
5563
$UserSite = "$(python -m site --user-site)"
56-
cd "$UserSite/.."
57-
python Scripts/pywin32_postinstall.py -install -destination "$UserSite"
58-
python Scripts/pywin32_postinstall.py -remove -destination "$UserSite"
64+
python -m win32.scripts.pywin32_postinstall -install -destination "$UserSite"
65+
pywin32_postinstall -remove -destination "$UserSite"
5966
6067
- name: Run tests
6168
# Run the tests directly from the source dir so support files (eg, .wav files etc)
6269
# can be found - they aren't installed into the Python tree.
63-
run: python pywin32_testall.py -v -skip-adodbapi
70+
run: python win32/scripts/pywin32_testall.py -v -skip-adodbapi
6471

6572
- name: Build wheels
6673
run: pip wheel . -v --wheel-dir=dist

CHANGES.txt

+4-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,10 @@ Coming in build 309, as yet unreleased
1515
--------------------------------------
1616

1717
* Added support for relative path for `pywin32_postinstall`'s `-destination` argument (#2454, @Avasam)
18-
* Changed the implementation of 'com_record' to a subclassable Python type (2437#, #2361, @geppi)
18+
* The postinstall script is now available as a console script. You can invoke it in one of two new methods: (#2408, @Avasam)
19+
1. `python -m pywin32_postinstall -install` (recommended)
20+
2. `pywin32_postinstall -install` (shorter but you don't have control over which python environment is used)
21+
* Changed the implementation of 'com_record' to a subclassable Python type (#2437, #2361, @geppi)
1922
* Removed param `hIcon` from `win32comext.shell.ShellExecuteEx`. It was unusable since Windows Vista (#2423, @Avasam)
2023
* Fixed `nbios.NCBStruct` packing (#2406, @Avasam)
2124
* Restored axdebug builds on Python 3.10 (#2416, @Avasam)

Pythonwin/win32view.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -839,8 +839,8 @@ static PyObject *ui_edit_window_save_file(PyObject *self, PyObject *args)
839839
CDocument *pDocument = pView->GetDocument();
840840
if (pDocument)
841841
pDocument->SetModifiedFlag(FALSE); // start off with unmodified
842-
PyWinObject_FreeTCHAR(fileName);
843842
GUI_END_SAVE;
843+
PyWinObject_FreeTCHAR(fileName);
844844
RETURN_NONE;
845845
}
846846
// @pymethod tuple|PyCEditView|PreCreateWindow|Calls the underlying MFC PreCreateWindow method.

Pythonwin/win32win.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -1654,9 +1654,9 @@ static PyObject *ui_window_message_box(PyObject *self, PyObject *args)
16541654
// @pyseemfc CWnd|MessageBox
16551655

16561656
rc = pWnd->MessageBox(message, title, style);
1657+
GUI_END_SAVE;
16571658
PyWinObject_FreeTCHAR(message);
16581659
PyWinObject_FreeTCHAR(title);
1659-
GUI_END_SAVE;
16601660
return Py_BuildValue("i", rc);
16611661
// @rdesc An integer identifying the button pressed to dismiss the dialog.
16621662
}

README.md

+20-5
Original file line numberDiff line numberDiff line change
@@ -22,13 +22,18 @@ The docs are a long and sad story, but [there's now an online version](https://m
2222
of the `PyWin32.chm` helpfile (thanks [@ofek](https://github.com/mhammond/pywin32/pull/1774)!).
2323
Lots of that is very old, but some is auto-generated and current. Would love help untangling the docs!
2424

25+
You can get type hints, signatures and annotations from [`types-pywin32`](https://pypi.org/project/types-pywin32/).
26+
2527
## Support
2628

2729
Feel free to [open issues](https://github.com/mhammond/pywin32/issues) for
2830
all bugs (or suspected bugs) in pywin32. [pull-requests](https://github.com/mhammond/pywin32/pulls)
2931
for all bugs or features are also welcome.
3032

31-
However, please **do not open github issues for general support requests**, or
33+
Type stubs currently live in [typeshed](<https://github.com/python/typeshed/tree/main/stubs/pywin32>).
34+
Any issue or request related to static type-checking and IntelliSense should be raised there.
35+
36+
However, please **do not open GitHub issues for general support requests**, or
3237
for problems or questions using the modules in this package - they will be
3338
closed. For such issues, please email the
3439
[python-win32 mailing list](https://mail.python.org/mailman/listinfo/python-win32) -
@@ -53,18 +58,22 @@ There is a post-install script (see below) which should *not* be run inside virt
5358
it should only be run in "global" installs.
5459

5560
For unreleased changes, you can download builds made by [github actions](https://github.com/mhammond/pywin32/actions/) -
56-
choose any "workflow" from the `main` branch and download its "artifacts")
61+
choose any "workflow" from the `main` branch and download its "artifacts"
5762

5863
### Installing globally
5964

6065
Outside of a virtual environment you might want to install COM objects, services, etc. You can do
6166
this by executing:
6267

6368
```shell
64-
python Scripts/pywin32_postinstall.py -install
69+
python -m pywin32_postinstall -install
6570
```
6671

67-
From the root of your Python installation.
72+
or (shorter but you don't have control over which python environment is used)
73+
74+
```shell
75+
pywin32_postinstall -install
76+
```
6877

6978
If you do this with normal permissions it will be global for your user (a few files will be
7079
copied to the root of your Python install and some changes made to HKCU). If you execute this from
@@ -103,7 +112,13 @@ It usually means one of 2 things:
103112
So you should run it again:
104113

105114
```shell
106-
python Scripts/pywin32_postinstall.py -install
115+
python -m pywin32_postinstall -install
116+
```
117+
118+
or (shorter but you don't have control over which python environment is used)
119+
120+
```shell
121+
pywin32_postinstall -install
107122
```
108123

109124
This will make some small attempts to cleanup older conflicting installs.

com/win32com/client/CLSIDToClass.py

+3-1
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,9 @@
1717
Access.
1818
"""
1919

20-
mapCLSIDToClass = {}
20+
from __future__ import annotations
21+
22+
mapCLSIDToClass: dict[str, type] = {}
2123

2224

2325
def RegisterCLSID(clsid, pythonClass):

com/win32com/client/__init__.py

+45-56
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
# Note that if the unknown dispatch object then returns a known
66
# dispatch object, the known class will be used. This contrasts
77
# with dynamic.Dispatch behaviour, where dynamic objects are always used.
8+
from __future__ import annotations
89

910
import sys
1011
from itertools import chain
@@ -263,7 +264,39 @@ def __setattr__(self, attr, val):
263264
setattr(self._obj_, attr, val)
264265

265266

266-
def DispatchWithEvents(clsid, user_event_class):
267+
def __get_disp_and_event_classes(dispatch):
268+
# Create/Get the object.
269+
disp = Dispatch(dispatch)
270+
271+
if disp.__class__.__dict__.get("CLSID"):
272+
return disp.__class__
273+
274+
# Eeek - no makepy support - try and build it.
275+
error_msg = "This COM object can not automate the makepy process - please run makepy manually for this object"
276+
try:
277+
ti = disp._oleobj_.GetTypeInfo()
278+
disp_clsid = ti.GetTypeAttr()[0]
279+
tlb, index = ti.GetContainingTypeLib()
280+
tla = tlb.GetLibAttr()
281+
gencache.EnsureModule(tla[0], tla[1], tla[3], tla[4], bValidateFile=0)
282+
# Get the class from the module.
283+
disp_class = gencache.GetClassForProgID(str(disp_clsid))
284+
except pythoncom.com_error as error:
285+
raise TypeError(error_msg) from error
286+
287+
if disp_class is None:
288+
raise TypeError(error_msg)
289+
# Get the clsid
290+
clsid = disp_class.CLSID
291+
# Create a new class that derives from 2 classes:
292+
# the event sink class and the user class.
293+
events_class = getevents(clsid)
294+
if events_class is None:
295+
raise ValueError("This COM object does not support events.")
296+
return disp, disp_class, events_class
297+
298+
299+
def DispatchWithEvents(clsid, user_event_class) -> EventsProxy:
267300
"""Create a COM object that can fire events to a user defined class.
268301
clsid -- The ProgID or CLSID of the object to create.
269302
user_event_class -- A Python class object that responds to the events.
@@ -302,39 +335,14 @@ class object that derives from three classes:
302335
Visible changed: 1
303336
>>>
304337
"""
305-
# Create/Get the object.
306-
disp = Dispatch(clsid)
307-
if not disp.__class__.__dict__.get(
308-
"CLSID"
309-
): # Eeek - no makepy support - try and build it.
310-
try:
311-
ti = disp._oleobj_.GetTypeInfo()
312-
disp_clsid = ti.GetTypeAttr()[0]
313-
tlb, index = ti.GetContainingTypeLib()
314-
tla = tlb.GetLibAttr()
315-
gencache.EnsureModule(tla[0], tla[1], tla[3], tla[4], bValidateFile=0)
316-
# Get the class from the module.
317-
disp_class = gencache.GetClassForProgID(str(disp_clsid))
318-
except pythoncom.com_error:
319-
raise TypeError(
320-
"This COM object can not automate the makepy process - please run makepy manually for this object"
321-
)
322-
else:
323-
disp_class = disp.__class__
324-
# If the clsid was an object, get the clsid
325-
clsid = disp_class.CLSID
326-
# Create a new class that derives from 3 classes - the dispatch class, the event sink class and the user class.
327-
events_class = getevents(clsid)
328-
if events_class is None:
329-
raise ValueError("This COM object does not support events.")
338+
disp, disp_class, events_class = __get_disp_and_event_classes(clsid)
330339
result_class = type(
331340
"COMEventClass",
332341
(disp_class, events_class, user_event_class),
333342
{"__setattr__": _event_setattr_},
334343
)
335-
instance = result_class(
336-
disp._oleobj_
337-
) # This only calls the first base class __init__.
344+
# This only calls the first base class __init__.
345+
instance = result_class(disp._oleobj_)
338346
events_class.__init__(instance, instance)
339347
if hasattr(user_event_class, "__init__"):
340348
user_event_class.__init__(instance)
@@ -365,33 +373,14 @@ def WithEvents(disp, user_event_class):
365373
This is mainly useful where using DispatchWithEvents causes
366374
circular reference problems that the simple proxy doesn't deal with
367375
"""
368-
disp = Dispatch(disp)
369-
if not disp.__class__.__dict__.get(
370-
"CLSID"
371-
): # Eeek - no makepy support - try and build it.
372-
try:
373-
ti = disp._oleobj_.GetTypeInfo()
374-
disp_clsid = ti.GetTypeAttr()[0]
375-
tlb, index = ti.GetContainingTypeLib()
376-
tla = tlb.GetLibAttr()
377-
gencache.EnsureModule(tla[0], tla[1], tla[3], tla[4], bValidateFile=0)
378-
# Get the class from the module.
379-
disp_class = gencache.GetClassForProgID(str(disp_clsid))
380-
except pythoncom.com_error:
381-
raise TypeError(
382-
"This COM object can not automate the makepy process - please run makepy manually for this object"
383-
)
384-
else:
385-
disp_class = disp.__class__
386-
# Get the clsid
387-
clsid = disp_class.CLSID
388-
# Create a new class that derives from 2 classes - the event sink
389-
# class and the user class.
390-
events_class = getevents(clsid)
391-
if events_class is None:
392-
raise ValueError("This COM object does not support events.")
393-
result_class = type("COMEventClass", (events_class, user_event_class), {})
394-
instance = result_class(disp) # This only calls the first base class __init__.
376+
disp, disp_class, events_class = __get_disp_and_event_classes(disp)
377+
result_class = type(
378+
"COMEventClass",
379+
(events_class, user_event_class),
380+
{},
381+
)
382+
# This only calls the first base class __init__.
383+
instance = result_class(disp)
395384
if hasattr(user_event_class, "__init__"):
396385
user_event_class.__init__(instance)
397386
return instance

com/win32com/src/extensions/PyIType.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -688,8 +688,8 @@ PyObject *pythoncom_loadtypelib(PyObject *self, PyObject *args)
688688
ITypeLib *ptl;
689689
PY_INTERFACE_PRECALL;
690690
SCODE sc = LoadTypeLib(bstrName, &ptl);
691-
PyWinObject_FreeBstr(bstrName);
692691
PY_INTERFACE_POSTCALL;
692+
PyWinObject_FreeBstr(bstrName);
693693
if (FAILED(sc))
694694
return PyCom_BuildPyException(sc);
695695

com/win32comext/bits/src/PyIBackgroundCopyJob.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -620,9 +620,9 @@ PyObject *PyIBackgroundCopyJob::SetProxySettings(PyObject *self, PyObject *args)
620620
HRESULT hr;
621621
PY_INTERFACE_PRECALL;
622622
hr = pIBCJ->SetProxySettings(ProxyUsage, ProxyList, ProxyBypassList);
623+
PY_INTERFACE_POSTCALL;
623624
PyWinObject_FreeWCHAR(ProxyList);
624625
PyWinObject_FreeWCHAR(ProxyBypassList);
625-
PY_INTERFACE_POSTCALL;
626626
if (FAILED(hr))
627627
return PyCom_BuildPyException(hr, pIBCJ, IID_IBackgroundCopyJob);
628628
Py_INCREF(Py_None);

com/win32comext/internet/src/PyIDocHostUIHandler.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -376,8 +376,8 @@ PyObject *PyIDocHostUIHandler::TranslateUrl(PyObject *self, PyObject *args)
376376
HRESULT hr;
377377
PY_INTERFACE_PRECALL;
378378
hr = pIDHUIH->TranslateUrl(dwTranslate, pchURLIn, &pchURLOut);
379-
PyWinObject_FreeWCHAR(pchURLIn);
380379
PY_INTERFACE_POSTCALL;
380+
PyWinObject_FreeWCHAR(pchURLIn);
381381
if (FAILED(hr))
382382
return PyCom_BuildPyException(hr, pIDHUIH, IID_IDocHostUIHandler);
383383
PyObject *pyretval = MakeOLECHARToObj(pchURLOut);

com/win32comext/internet/src/PyIInternetSecurityManager.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -145,8 +145,8 @@ PyObject *PyIInternetSecurityManager::ProcessUrlAction(PyObject *self, PyObject
145145
PY_INTERFACE_PRECALL;
146146
hr = pIISM->ProcessUrlAction(pwszUrl, dwAction, (BYTE *)&dwPolicy, sizeof(dwPolicy), (BYTE *)context, cbcontext,
147147
dwFlags, 0);
148-
PyWinObject_FreeWCHAR(pwszUrl);
149148
PY_INTERFACE_POSTCALL;
149+
PyWinObject_FreeWCHAR(pwszUrl);
150150
if (FAILED(hr))
151151
return PyCom_BuildPyException(hr, pIISM, IID_IInternetSecurityManager);
152152
return Py_BuildValue("ll", hr, dwPolicy);

com/win32comext/shell/src/PyICopyHook.cpp

+3-4
Original file line numberDiff line numberDiff line change
@@ -54,11 +54,10 @@ PyObject *PyICopyHookA::CopyCallback(PyObject *self, PyObject *args)
5454
HRESULT hr;
5555
PY_INTERFACE_PRECALL;
5656
hr = pICH->CopyCallback(hwnd, wFunc, wFlags, srcFile, srcAttribs, destFile, destAttribs);
57+
PY_INTERFACE_POSTCALL;
5758
PyWinObject_FreeChars(srcFile);
5859
PyWinObject_FreeChars(destFile);
5960

60-
PY_INTERFACE_POSTCALL;
61-
6261
if (FAILED(hr))
6362
return PyCom_BuildPyException(hr, pICH, IID_IShellCopyHook);
6463
Py_INCREF(Py_None);
@@ -143,10 +142,10 @@ PyObject *PyICopyHookW::CopyCallback(PyObject *self, PyObject *args)
143142
HRESULT hr;
144143
PY_INTERFACE_PRECALL;
145144
hr = pICH->CopyCallback(hwnd, wFunc, wFlags, srcFile, srcAttribs, destFile, destAttribs);
146-
PyWinObject_FreeWCHAR(srcFile);
147-
PyWinObject_FreeWCHAR(destFile);
148145

149146
PY_INTERFACE_POSTCALL;
147+
PyWinObject_FreeWCHAR(srcFile);
148+
PyWinObject_FreeWCHAR(destFile);
150149

151150
if (FAILED(hr))
152151
return PyCom_BuildPyException(hr, pICH, IID_IShellCopyHook);

com/win32comext/shell/src/PyIExtractIcon.cpp

+2-1
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,9 @@ PyObject *PyIExtractIcon::Extract(PyObject *self, PyObject *args)
3838
HRESULT hr;
3939
PY_INTERFACE_PRECALL;
4040
hr = pIEI->Extract(pszFile, nIconIndex, &hiconLarge, &hiconSmall, nIconSize);
41-
PyWinObject_FreeChars(pszFile);
4241
PY_INTERFACE_POSTCALL;
42+
PyWinObject_FreeChars(pszFile);
43+
4344
if (FAILED(hr))
4445
return PyCom_BuildPyException(hr, pIEI, IID_IExtractIcon);
4546
if (hr == S_FALSE)

com/win32comext/shell/src/PyIExtractIconW.cpp

+2-1
Original file line numberDiff line numberDiff line change
@@ -42,8 +42,9 @@ PyObject *PyIExtractIconW::Extract(PyObject *self, PyObject *args)
4242
HRESULT hr;
4343
PY_INTERFACE_PRECALL;
4444
hr = pIEI->Extract(pszFile, nIconIndex, &hiconLarge, &hiconSmall, nIconSize);
45-
PyWinObject_FreeWCHAR(pszFile);
4645
PY_INTERFACE_POSTCALL;
46+
PyWinObject_FreeWCHAR(pszFile);
47+
4748
if (FAILED(hr))
4849
return PyCom_BuildPyException(hr, pIEI, IID_IExtractIconW);
4950
if (hr == S_FALSE)

0 commit comments

Comments
 (0)