"""https://stackoverflow.com/a/58241294/6235116"""

import pefile  # type: ignore
from datetime import datetime
import logging
from more_itertools import first
from contextlib import suppress

logging.basicConfig(level=logging.DEBUG)
def get_portable_executable_info(file_path: str) -> dict:
    """
    Return a dictionary with ProductVersion and other params of portable executable.

    The Portable Executable (PE) format is a file format for executables, object code, DLLs and others used in 32-bit
    and 64-bit versions of Windows operating systems. For non PE files this error is raised:
    pefile.PEFormatError: 'DOS Header magic not found.'
    We can use it to provide the info about the tested build to the pytest report.
    """
    decoded_dict: dict = {}  # To avoid "Local variable 'pe' might be referenced before assignment" alert.
    try:
        pe = pefile.PE(file_path)  # type - class 'pefile.PE'
    except FileNotFoundError as error:
        logging.error(f"Check the provided file path: {error}. An empty dict is returned.")
        return decoded_dict
    except pefile.PEFormatError as error:
        logging.error(f"The provided file is not Portable Executable: {error}. An empty dict is returned.")
        return decoded_dict

    file_info: list = pe.FileInfo 

    # Option 1. LBYL
    for structure in file_info[0]:
        if not hasattr(structure, 'StringTable'):
            continue
        list_with_string_table_structure: list = structure.StringTable
        encoded_dict: dict = list_with_string_table_structure[0].entries
        decoded_dict = {key.decode(): value.decode() for key, value in encoded_dict.items()}
        break

    # Option 2 EAFP
    for structure in file_info[0]:
        with suppress(AttributeError):
            list_with_string_table_structure: list = structure.StringTable
            encoded_dict = list_with_string_table_structure[0].entries
            decoded_dict = {key.decode(): value.decode() for key, value in encoded_dict.items()}
            break

    # Option 3
    with suppress(ValueError):
        string_file_info_structure = first(structure for structure in file_info[0] if hasattr(structure, 'StringTable'))
        list_with_string_table_structure = string_file_info_structure.StringTable
        encoded_dict = list_with_string_table_structure[0].entries
        decoded_dict = {key.decode(): value.decode() for key, value in encoded_dict.items()}

    # Add modification time from FILE_HEADER
    time_date_stamp = pe.FILE_HEADER.TimeDateStamp
    modification_time = str(datetime.fromtimestamp(time_date_stamp))
    decoded_dict["Modification_time"] = modification_time

    return decoded_dict


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

 Public I believe the last version! I saved all approaches in one file for (my) learning purposes.

Share a link to this review

1.56% issue ratio

L26 Executing code in global scope

When you have some code in module scope, the code will be executed when someone imports that module - which makes the module completely non-reusable. Maybe it's not meant to be reusable today, but you'll have to refactor if you need to import the module later. Use if __name__ == '__main__' to detect whether module was imported or used as a standalone script.


Create new review request