diff --git a/pyproject.toml b/pyproject.toml index 5c15747..7c59e1b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "winregistry" -version = "1.1.1" +version = "0.0.0" description = "Library aimed at working with Windows registry" authors = ["Aleksandr Shpak "] readme = "README.md" diff --git a/winregistry.py b/winregistry.py index ef5266c..9445e73 100644 --- a/winregistry.py +++ b/winregistry.py @@ -3,7 +3,9 @@ import typing as tp import winreg from abc import ABC, abstractmethod +from collections import namedtuple from datetime import datetime, timedelta +from types import TracebackType _HIVE_ALIASES_MAPPING: dict[str, str] = { 'HKCR': "HKEY_CLASSES_ROOT", @@ -14,202 +16,333 @@ 'HKCC': "HKEY_CURRENT_CONFIG", 'HKDD': 'HKEY_DYN_DATA', } - - -def _cast_winreg(value: str) -> tp.Any: - if not (obj := getattr(winreg, value)): - raise ValueError(f'unknown value {value}') - return obj - - -def _normalize_wow64_32(value): - if isinstance(value, bool): - value = winreg.KEY_WOW64_32KEY if value else winreg.KEY_WOW64_64KEY - return value +RawKeyInfo = namedtuple('RawKeyInfo', ['child_keys_count', 'values_count', 'modified_at']) +RawValueInfo = namedtuple('RawValueInfo', ['data', 'type']) class _RegEntity(ABC): - def __init__(self, reg_key, name, wow64_32: bool | int = False): + def __init__( + self, + hkey: winreg.HKEYType, + name: str | None, + ) -> None: + self._key_obj = hkey self._name = name - self._reg_key = reg_key - self._wow = _normalize_wow64_32(wow64_32) - self._raw = self._get_raw() + self._raw_info = None - def __repr__(self): + def __repr__( + self, + ) -> str: return f'' @abstractmethod - def _get_raw(self): ... + def refresh( + self, + ) -> None: ... - @classmethod - @abstractmethod - def from_index(cls, reg_key, index, wow64_32: bool | int = False) -> tp.Self: ... + @property + def raw_info( + self, + ) -> tuple: + if not self._raw_info: + self.refresh() + return self._raw_info - def _get_access_key(self, winreg_access_mode) -> int: - return winreg_access_mode | self._wow +class Value( + _RegEntity, +): + raw_info: RawValueInfo -class RegValue(_RegEntity): @classmethod - def from_index(cls, reg_key, index, wow64_32: bool | int = False) -> tp.Self: - name, value, raw_type = winreg.EnumValue(reg_key, index) - wow = _normalize_wow64_32(wow64_32) - return RegValue( - reg_key, + def from_index( + cls, + hkey: winreg.HKEYType, + index: int, + ) -> tp.Self: + name, value, raw_type = winreg.EnumValue(hkey, index) + return Value( + hkey, name=name, - wow64_32=wow, ) - def _get_raw(self): - return winreg.QueryValueEx(self._reg_key, self._name) + def refresh( + self, + ) -> None: + self._raw_info = RawValueInfo(*winreg.QueryValueEx(self._key_obj, self._name)) @property - def data(self): - return self._raw[0] + def data( + self, + ) -> tp.Any: + return self._raw_info[0] @data.setter - def data(self, value): - winreg.SetValueEx(self._reg_key, self._name, 0, self.type, value) + def data( + self, + value: tp.Any, + ) -> None: + winreg.SetValueEx(self._key_obj, self._name, 0, self.type, value) @property - def type(self): - return self._raw[1] - - def delete(self): ... - - -class RegKey(_RegEntity): - def __init__(self, reg_key, name, wow64_32: bool | int = False): - super().__init__(reg_key, name, wow64_32) - - def _get_raw(self): - return winreg.QueryInfoKey(self._reg_key) + def type( + self, + ) -> int: + return self._raw_info[1] + + +class Key( + _RegEntity, +): + raw_info: RawKeyInfo + + def __init__( + self, + hkey: winreg.HKEYType, + *, + name: str | None, + access_mode: int = winreg.KEY_READ, + wow_64_32: int = winreg.KEY_WOW64_64KEY, + ) -> None: + self._access_mode = access_mode | wow_64_32 + self._wow_64_32 = wow_64_32 + super().__init__(hkey, name) + + def refresh( + self, + ) -> None: + self._raw_info = RawKeyInfo(*winreg.QueryInfoKey(self._key_obj)) @classmethod - def from_index(cls, reg_key, index, wow64_32: bool | int = False) -> tp.Self: - name = winreg.EnumKey(reg_key, index) - wow = _normalize_wow64_32(wow64_32) - return RegKey( + def from_index( + cls, + hkey: winreg.HKEYType, + index: int, + access_mode: int = winreg.KEY_READ, + wow_64_32: int = winreg.KEY_WOW64_64KEY, + ) -> tp.Self: + name = winreg.EnumKey(hkey, index) + return Key( winreg.OpenKey( - key=reg_key, + key=hkey, sub_key=name, reserved=0, - access=winreg.KEY_ALL_ACCESS | wow, + access=access_mode | wow_64_32, ), name=name, - wow64_32=wow, + wow_64_32=wow_64_32, ) - def close(self): - self._reg_key.Close() + # @property - def child_keys_count(self): - return self._raw[0] + def child_keys_count( + self, + ) -> int: + return self._raw_info.child_keys_count @property - def values_count(self): - return self._raw[1] + def values_count( + self, + ) -> int: + return self._raw_info.values_count @property - def modified_at(self): - return datetime(1601, 1, 1) + timedelta(microseconds=self._raw[2] / 10) + def modified_at( + self, + ) -> datetime: + return datetime(1601, 1, 1) + timedelta(microseconds=self._raw_info.modified_at / 10) + + # @property - def child_keys_names(self): + def child_keys_names( + self, + ): for index in range(self.child_keys_count): - yield winreg.EnumKey(self._reg_key, index) + yield winreg.EnumKey(self._key_obj, index) @property - def child_keys(self): + def child_keys( + self, + ): for index in range(self.child_keys_count): - yield self.from_index(self._reg_key, index, self._wow) + yield self.from_index(self._key_obj, index, self._access_mode) @property - def values(self) -> tp.Generator[str, None]: + def values( + self, + ) -> tp.Generator[str, None]: for index in range(0, self.values_count): - yield RegValue.from_index(self._reg_key, index, self._wow) + yield Value.from_index(self._key_obj, index) + + # - def open_key(self, name: str) -> tp.Self: - return RegKey( + def open_subkey( + self, + name: str, + access_mode, + ) -> tp.Self: + return Key( winreg.OpenKey( - key=self._reg_key, + key=self._key_obj, sub_key=name, reserved=0, - access=self._get_access_key(winreg.KEY_ALL_ACCESS), + access=access_mode | self._wow_64_32, ), name=name, ) - def create_key(self, name): + def create_subkey( + self, + name: str, + access_mode: int = winreg.KEY_READ, + ) -> None: winreg.CreateKeyEx( - key=self._reg_key, + key=self._key_obj, sub_key=name, reserved=0, - access=self._get_access_key(winreg.KEY_WRITE), + access=access_mode, ) - def delete_key(self, name, recursive=False): + def delete_subkey( + self, + name: str, + recursive: bool = False, + ) -> None: if not recursive: - winreg.DeleteKey(self._reg_key, name) - - def read_value(self, name): - return RegValue(self._reg_key, name, self._wow) - - def set_value(self, name, value): ... - - def delete_value(self, name): ... - - def __enter__(self): + winreg.DeleteKey(self._key_obj, name) + return + # handle = self._get_handler(name, KEY_READ, key_wow64_32key) + # keys_num, values_num, modify = QueryInfoKey(handle) # pylint: disable=unused-variable + # for key_i in range(0, keys_num): + # key = EnumKey(handle, key_i) + # self.delete_key_tree(f"{name}\\{key}", key_wow64_32key) + # handle.Close() + # self.delete_key(name, key_wow64_32key) + + # + + def read_value( + self, + name: str, + ) -> Value: + return Value(self._key_obj, name) + + def set_value( + self, + name: str, + value: tp.Any, + type: int, + ) -> Value: ... + + def unset_value( + self, + name: str, + ) -> None: + winreg.DeleteValue(self._key_obj, name) + + # + + def close( + self, + ) -> None: + self._key_obj.Close() + + def __enter__( + self, + ) -> tp.Self: return self - def __exit__(self, exc_type, exc_val, exc_tb): + def __exit__( + self, + exc_type: type[BaseException] | None, + exc_value: BaseException | None, + traceback: TracebackType | None, + ) -> None: self.close() -class WinRegistry(RegKey): - @classmethod - def connect(cls, name: str | int, host=None, wow64_32=False): - if isinstance(name, int): - return cls(winreg.ConnectRegistry(host, name), wow64_32) - return cls( - winreg.ConnectRegistry(host, _cast_winreg(name.upper())), - wow64_32, - ) - - -# class robot: -# def __init__(self, root, host=None, wow64_32=False): -# self._cls_params = { -# 'wow64_32': wow64_32, -# 'host': host, -# } -# -# def create_registry_key(self, key_name: str, wow64_32: bool = False) -> None: -# """Creating registry key""" -# with WinRegistry(**self._cls_params) as client: -# client.create_key(key, wow64_32) -# -# def delete_registry_key(self, key_name: str) -> None: -# """Deleting registry key""" -# with WinRegistry(**self._cls_params) as client: -# client.delete_key(key, wow64_32) -# -# def read_registry_value(self, key: str, value: tp.Any) -> RegEntry: -# """Reading value from registry""" -# with WinRegistry(**self._cls_params) as client: -# return client.read_entry(key, value) -# -# def set_registry_value(self, key: str, value: str, data: tp.Any = None, reg_type: str = "REG_SZ") -> None: -# """Writing (or creating) data in value""" -# with WinRegistry(**self._cls_params) as client: -# client.write_entry(key, value, data, 'WinregType'[reg_type], wow64_32) -# -# def delete_registry_value(self, key: str, value: str, wow64_32: bool = False) -> None: -# """Deleting value from registry""" -# with WinRegistry(**self._cls_params) as client: -# client.delete_entry(key, value, wow64_32) - - -__all__ = ['WinRegistry', 'RegKey', 'RegValue'] +def registry_connect( + key, + host=None, + access_mode=winreg.KEY_READ, + wow_64_32=winreg.KEY_WOW64_64KEY, +) -> Key: + return Key( + hkey=winreg.ConnectRegistry(host, key), + name=None, + access_mode=access_mode, + wow_64_32=wow_64_32, + ) + + +class robot: + def __init__( + self, + root: str, + host=None, + wow64_32=False, + ) -> None: + self._cls_params = { + 'root': root, + 'wow64_32': wow64_32, + 'host': host, + } + + def create_registry_key(self, key_name: str) -> None: + """Creating registry key""" + with registry_connect(**self._cls_params) as key: + key.create_subkey(key_name) + + def delete_registry_key(self, key_name: str) -> None: + """Deleting registry key""" + with registry_connect(**self._cls_params) as client: + client.delete_subkey(key_name) + + def read_registry_value(self, keyname: str, value_name: str) -> tp.Any: + """Reading value from registry""" + with registry_connect(**self._cls_params) as client: + return client.read_value(value_name) + + def create_registry_value( + self, + key_name: str, + value_name: str, + data: tp.Any = None, + reg_type: str = "REG_SZ", + ) -> None: + """Writing (or creating) data in value""" + with registry_connect(**self._cls_params) as client: + client.set_value(value_name, data) + + def set_registry_value( + self, + key_name: str, + value_name: str, + data: tp.Any = None, + reg_type: str = "REG_SZ", + ) -> None: + """Writing (or creating) data in value""" + with registry_connect(**self._cls_params) as client: + client.set_value(value_name, data) + + def unset_registry_value( + self, + key: str, + value: str, + wow64_32: bool = False, + ) -> None: + """Deleting value from registry""" + with registry_connect(**self._cls_params) as client: + client.unset_value(key, value, wow64_32) + + +__all__ = [ + 'registry_connect', + 'Key', + 'Value', + 'RawKeyInfo', + 'RawValueInfo', +] __version__ = '0.0.0'