import winreg
import logging
from re import search


def registry_open_key(registry_path: str) -> winreg.HKEYType | bool:
    """
    Opens specified in registry_path key for winreg operations.

    :param registry_path: str. Any format of the path to the registry, like Computer\HKEY_CURRENT_USER\Software\Folder
    :return: a PyHKEY key, open for winreg operations or False if key cannot be open
    """

    # extract registry folder name from the full path: HKEY_CURRENT_USER etc.
    hkey_folder: str = search(r"HKEY[^\\]*", registry_path)[0]  # type: ignore
    logging.debug(f"The registry folder is {hkey_folder=}")

    # find the folder name in the provided path
    path_to_sub_key: str = search(f"(?<={hkey_folder}\\\).*", registry_path)[0]  # type: ignore
    logging.debug(f"The sub path is {path_to_sub_key=}")

    # Need to use the getattr, because hkey is string, so I can't get access to winreg."HKEY_CURRENT_USER."
    hkey_constant: int = getattr(winreg, hkey_folder)  # = winreg.HKEY_CURRENT_USER (for example)
    logging.debug(f"{hkey_constant=} for {hkey_folder}")

    opened_key = False  # To avoid "Local variable might be referenced before assignment" alert.
    try:
        opened_key = winreg.OpenKeyEx(hkey_constant, path_to_sub_key, access=winreg.KEY_WRITE)
        logging.debug(f"Successfully opened: {registry_path}")
    except FileNotFoundError as error:
        logging.error(f"Check the provided registry path: {error}")
    except WindowsError as error:  # The same as PermissionError
        logging.error(f"Run the script as admin: {error}")

    return opened_key


def registry_create_value(registry_path: str, value_type: str, value_name: str, value_data: str, ) -> bool:
    """
    Creates value with provided name, type (REG_SZ etc) and data.

    :param registry_path: str.
    :param value_type: str. REG_SZ for String Value, REG_BINARY for Binary Value etc.
    :param value_name: str.
    :param value_data: str.
    :return: True if the value was created, False otherwise.
    """

    # Checking that value_type parameter has the right format.
    value_type_int = 0  # To avoid "Local variable might be referenced before assignment" alert.
    try:
        value_type_int: int = getattr(winreg, value_type)
        logging.debug("Value_type successfully checked")
    except AttributeError as error:
        logging.error(f"Check the provided value_type parameter: {error}")

    key_is_ready = registry_open_key(registry_path)
    if not key_is_ready:
        logging.error("Key is not open")
        return False
    else:
        logging.debug(f"Ready to work with the key {registry_path}")
        # Creating a new value_name and value_data
        winreg.SetValueEx(key_is_ready, value_name, 0, value_type_int, value_data)
        logging.debug(f"{value_name} was created")
        return True


def registry_delete_value(registry_path: str, value_name: str) -> bool:
    """
    Removes a named value from a registry key.

    :param registry_path: str
    :param value_name: str
    :return: True if the value was removed, False otherwise
    """

    key_is_ready = registry_open_key(registry_path)
    if not key_is_ready:
        logging.error("Key is not open")
        return False
    else:
        logging.debug(f"Ready to work with the key {registry_path}")
        try:
            winreg.DeleteValue(key_is_ready, value_name)
            logging.debug(f"Key {value_name} deleted")
            return True
        except FileNotFoundError as error:
            logging.error(f"Check the provided value_name parameter: {error}")
            return False


def registry_create_key():
    """
    ToDo
    :return:
    """

    pass


def registry_delete_key():
    """
    ToDo
    :return:
    """

    pass



if __name__ == '__main__':
    logging.basicConfig(format=FORMAT, level=logging.DEBUG)

 Public А draft for scripts that will be used for Win Registry tests

Share a link to this review

3.54% issue ratio

L9 Bad design

Seems like things could be organized in a better way.

It's more common to return None in case of some errors. Not False. Also see the refactored version which tries to return ASAP and uses implicit "return None".

Suggested change:
def registry_open_key(registry_path: str) -> winreg.HKEYType | None:
    # ...
    # opened_key = False  <-- this is not needed!
    try:
        opened_key = winreg.OpenKeyEx(hkey_constant, path_to_sub_key, access=winreg.KEY_WRITE)
        logging.debug(f"Successfully opened: {registry_path}")
        return opened_key  # <-- we can return it immediately
    except FileNotFoundError as error:
        logging.error(f"Check the provided registry path: {error}")
    except WindowsError as error:  # The same as PermissionError
        logging.error(f"Run the script as admin: {error}")
    # here is end of function; if function ends with nothing, it implicitly returns `None`
L12 Redundant code / overengineering

This code is not really needed or may be simplified

try-catch not needed here.

Suggested change:
value_type_int = getattr(winreg, value_type, 0)  # use `0` as default
R7 Redundant "else"

There is a well-known pattern "if a: return one else: return two". In fact, the else keyword is redundant, because when first if condition evaluates to True, the function returns, otherwise control flow goes on. Removing else makes the code cleaner and easier to read.

Suggested change:
key_is_ready = registry_open_key(registry_path)
if not key_is_ready:
    logging.error("Key is not open")
    return False
    
logging.debug(f"Ready to work with the key {registry_path}")
# Creating a new value_name and value_data
winreg.SetValueEx(key_is_ready, value_name, 0, value_type_int, value_data)
logging.debug(f"{value_name} was created")
return True
R7 Redundant "else"

There is a well-known pattern "if a: return one else: return two". In fact, the else keyword is redundant, because when first if condition evaluates to True, the function returns, otherwise control flow goes on. Removing else makes the code cleaner and easier to read.


Create new review request