|
import re |
|
import winreg |
|
from pathlib import Path |
|
from ctypes import windll |
|
from configparser import ConfigParser |
|
|
|
|
|
def replace_placeholder(s): |
|
"""Replace all placeholder with the correct string""" |
|
return re.sub(r"%[a-zA-Z]+%", lambda x: winreg.ExpandEnvironmentStrings(x[0]), s) |
|
|
|
|
|
def match_any_parent(path, pattern): |
|
"""Matches every parent of path against pattern""" |
|
return any(p.match(pattern) for p in path.parents) |
|
|
|
|
|
def get_user_path(reg): |
|
""" |
|
Get user path variable |
|
returns {Path: str} where Path is the expanded form while str is the raw string |
|
""" |
|
env_key = winreg.OpenKeyEx(reg, "Environment", access=winreg.KEY_QUERY_VALUE) |
|
path, regtype = winreg.QueryValueEx(env_key, "PATH") |
|
if regtype != winreg.REG_EXPAND_SZ: |
|
raise Exception("PATH not found in registry") |
|
# remove ; and whitespaces so later we don't have to |
|
path = path.strip(" ;") |
|
fullpath = replace_placeholder(path) |
|
return {Path(p): q for p, q in zip(fullpath.split(";"), path.split(";"))} |
|
|
|
|
|
def set_user_path(reg, path): |
|
"""Set user path and send WM_SETTINGSCHANGE""" |
|
env_key = winreg.OpenKeyEx(reg, "Environment", access=winreg.KEY_SET_VALUE) |
|
winreg.SetValueEx(env_key, "PATH", 0, winreg.REG_EXPAND_SZ, ";".join(path)) |
|
winreg.FlushKey(env_key) |
|
# 0xFFFF - HWND_BROADCAST |
|
# 0x001A - WM_SETTINGSCHANGE |
|
windll.user32.SendMessageA(0xFFFF, 0x001A, None, "Environment") |
|
|
|
|
|
def get_current_path(path, excl): |
|
"""Get candidates to add to PATH""" |
|
return { |
|
p.parent |
|
for p in path.rglob("*.exe") |
|
if not any(match_any_parent(p, e) for e in excl) |
|
} |
|
|
|
|
|
def main(): |
|
"""Main function""" |
|
config = ConfigParser() |
|
config.read("config.ini") |
|
cwd = Path(config["DEFAULT"]["path"]).expanduser() |
|
excludes = { |
|
name.strip() for name in config["DEFAULT"]["excludes"].split(",") if name |
|
} |
|
|
|
# read the registry |
|
reg = winreg.ConnectRegistry(None, winreg.HKEY_CURRENT_USER) |
|
user_path = get_user_path(reg) |
|
current_path = get_current_path(cwd, excludes) |
|
user_path_set = set(user_path.keys()) |
|
# path that is related (managed) |
|
related_path = {p for p in user_path_set if cwd in p.parents} |
|
# unrelated path (not managed) |
|
unrelated_path = user_path_set - related_path |
|
|
|
if related_path != current_path: |
|
# paths that should be removed from env |
|
rem_path = {p for p in related_path if p not in current_path} |
|
new_path = unrelated_path | current_path - rem_path |
|
# replace some Path instances with value in registry |
|
new_path = sorted(user_path.get(p, str(p)) for p in new_path) |
|
|
|
print("DIRECTORIES TO ADD") |
|
print("==================") |
|
for p in related_path - current_path: |
|
print(p) |
|
print("DIRECTORIES TO REMOVE") |
|
print("=====================") |
|
for p in rem_path: |
|
print(p) |
|
|
|
set_user_path(reg, new_path) |
|
print("PATH set!") |
|
else: |
|
print("No change detected") |
|
|
|
|
|
if __name__ == "__main__": |
|
main() |