Skip to content

Commit

Permalink
477 Move clear_comtypes_cache to be a callable module (#478)
Browse files Browse the repository at this point in the history
* move clear_comtypes_cache to be a callable module

This commit modifies the clear_comtypes_cache.py script so that it is inside
the main comtypes module (renamed as just clear_cache) so that is can be called
more easily as "py -m comtypes.clear_cache". The main function of the script is
also exported using the "console_scripts" entry point so that the script also
goes into the standard Python "Scripts" folder as before, but now as a .exe
instead of a .py script, which makes it easier to run if systems are set to
open .py files instead of running them.

This version also includes a test case using the 3rd party package pyfakefs.
Currently, this is not desired to avoid the requirement of 3rd party packages
in comtypes, but is included here for potential use if the position changes.
A subsequent commit will modify the tests to use unittest.patch instead, which
is an inferior technical solution but avoids a 3rd party package.

* modify clear_cache tests to not use pyfakefs

This commit updates the test for comtypes.clear_cache to not use any 3rd party
packages, instead relying on mocking the shutil.rmtree function which is used
to do the actual cache deletion.

* change quotes in print string

* style changes based on review by @junkmd

* Apply suggestions from code review

Co-authored-by: Jun Komoda <[email protected]>

---------

Co-authored-by: Ben Rowland <[email protected]>
Co-authored-by: Jun Komoda <[email protected]>
  • Loading branch information
3 people authored Jun 13, 2023
1 parent 62ce303 commit 8954f61
Show file tree
Hide file tree
Showing 5 changed files with 84 additions and 65 deletions.
2 changes: 1 addition & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ Those `.py` files act like ”caches”.

If there are some problems with the developing code base, partial or non-executable modules might be created in `.../comtypes/gen/...`.
Importing them will cause some error.
If that happens, you should run `python -m clear_comtypes_cache` to clear those caches.
If that happens, you should run `python -m comtypes.clear_cache` to clear those caches.
The command will delete the entire `.../comtypes/gen` directory.
Importing `comtypes.gen.client` will restore the directory and `__init__.py` file.

Expand Down
57 changes: 0 additions & 57 deletions clear_comtypes_cache.py

This file was deleted.

53 changes: 53 additions & 0 deletions comtypes/clear_cache.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import argparse
import contextlib
import os
import sys
from shutil import rmtree # TESTS ASSUME USE OF RMTREE


# if supporting Py>=3.11 only, this might be `contextlib.chdir`.
# https://docs.python.org/3/library/contextlib.html#contextlib.chdir
@contextlib.contextmanager
def chdir(path):
"""Context manager to change the current working directory."""
work_dir = os.getcwd()
os.chdir(path)
yield
os.chdir(work_dir)


def main():
parser = argparse.ArgumentParser(
prog="py -m comtypes.clear_cache", description="Removes comtypes cache folders."
)
parser.add_argument(
"-y", help="Pre-approve deleting all folders", action="store_true"
)
args = parser.parse_args()

if not args.y:
confirm = input("Remove comtypes cache directories? (y/n): ")
if confirm.lower() != "y":
print("Cache directories NOT removed")
return

# change cwd to avoid import from local folder during installation process
with chdir(os.path.dirname(sys.executable)):
try:
import comtypes.client
except ImportError:
print("Could not import comtypes", file=sys.stderr)
sys.exit(1)

# there are two possible locations for the cache folder (in the comtypes
# folder in site-packages if that is writable, otherwise in APPDATA)
# fortunately, by deleting the first location returned by _find_gen_dir()
# we make it un-writable, so calling it again gives us the APPDATA location
for _ in range(2):
dir_path = comtypes.client._find_gen_dir()
rmtree(dir_path)
print(f'Removed directory "{dir_path}"')


if __name__ == "__main__":
main()
25 changes: 25 additions & 0 deletions comtypes/test/test_clear_cache.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
"""
Test for the ``comtypes.clear_cache`` module.
"""
import contextlib
import runpy
from unittest.mock import patch, call
from unittest import TestCase

from comtypes.client import _find_gen_dir


class ClearCacheTestCase(TestCase):
# we patch sys.stdout so unittest doesn't show the print statements

@patch("sys.argv", ["clear_cache.py", "-y"])
@patch("shutil.rmtree")
def test_clear_cache(self, mock_rmtree):
with contextlib.redirect_stdout(None):
runpy.run_module("comtypes.clear_cache", {}, "__main__")

# because we don't actually delete anything, _find_gen_dir() will
# give the same answer every time we call it
self.assertEqual(
mock_rmtree.call_args_list, [call(_find_gen_dir()) for _ in range(2)]
)
12 changes: 5 additions & 7 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -110,14 +110,10 @@ def run(self):
install.run(self)
# Custom script we run at the end of installing
if not self.dry_run and not self.root:
filename = os.path.join(
self.install_scripts, "clear_comtypes_cache.py")
if not os.path.isfile(filename):
raise RuntimeError("Can't find '%s'" % (filename,))
print("Executing post install script...")
print('"' + sys.executable + '" "' + filename + '" -y')
print(f'"{sys.executable}" -m comtypes.clear_cache -y')
try:
subprocess.check_call([sys.executable, filename, '-y'])
subprocess.check_call([sys.executable, "-m", "comtypes.clear_cache", '-y'])
except subprocess.CalledProcessError:
print("Failed to run post install script!")

Expand Down Expand Up @@ -145,7 +141,9 @@ def run(self):
]},
classifiers=classifiers,

scripts=["clear_comtypes_cache.py"],
entry_points={
"console_scripts": ["clear_comtypes_cache=comtypes.clear_cache:main"]
},

cmdclass={
'test': test,
Expand Down

0 comments on commit 8954f61

Please sign in to comment.