2
Day10 homework Karen
Karenzz edited this page 2023-04-20 00:56:16 +02:00

This is an extension to the day 9 homework. First, rebase your branch onto the main branch of this repository: git rebase main.

1. Run the module-inspector script, and fix any problems that prevent it from running. 2. Look at the output. Notice that some of the module names seem similar, and are cached via two different loader methods. 3. Based on what you see from the module-inspector script, modify your answer from question 10 from the day 9 homework, so the similar module names aren't counted multiple times.

import os
import sys
import importlib.util
import types
from collections import defaultdict
from sys import getsizeof


def get_bytecode_size(module_name):
    """Get the size of the cached .pyc bytecode file for a module."""
    spec = importlib.util.find_spec(module_name)
    if spec and spec.origin and spec.origin.endswith('.py'):
        pyc_file = importlib.util.cache_from_source(spec.origin)
        if os.path.exists(pyc_file):
            return os.path.getsize(pyc_file)
    return 0


def get_dso_size(module_name):
    """Get the size of the DSO (Dynamic Shared Object) module file for a module."""
    spec = importlib.util.find_spec(module_name)
    if spec and spec.origin and spec.origin.endswith(('.so', '.dll')):
        return os.path.getsize(spec.origin)
    return 0


def generate_module_report():
    """Generate a report summarizing the module environment."""
    total_modules = len(sys.modules)
    bytecode_size = sum(get_bytecode_size(module_name) for module_name in sys.modules)
    dso_size = sum(get_dso_size(module_name) for module_name in sys.modules)
    python_size = sum(
        getsizeof(sys.modules[module_name]) - getsizeof(module_name)
        for module_name in sys.modules
        if isinstance(sys.modules[module_name], types.ModuleType)
    )
    total_size = bytecode_size + dso_size + python_size

    # Calculate percentage of each type of module
    bytecode_percent = bytecode_size / total_size * 100
    dso_percent = dso_size / total_size * 100
    python_percent = python_size / total_size * 100

    # Generate the report
    report = f"Total modules: {total_modules}\n"
    report += f"Total size: {total_size} bytes\n"
    report += f"Bytecode modules: {bytecode_percent:.2f}% ({bytecode_size} bytes)\n"
    report += f"DSO modules: {dso_percent:.2f}% ({dso_size} bytes)\n"
    report += f"Python code modules: {python_percent:.2f}% ({python_size} bytes)\n"

    # Generate histogram of module directory names
    dir_histogram = defaultdict(int)
    for module_name in sys.modules:
        module_file = getattr(sys.modules[module_name], '__file__', None)
        if module_file:
            module_dir = os.path.dirname(module_file)
            dir_histogram[module_dir] += 1

    report += "\nModule Directory Histogram:\n"
    for module_dir, count in dir_histogram.items():
        report += f"{module_dir}: {'*' * count}\n"

    return report


if __name__ == "__main__":
    __spec__ = importlib.util.spec_from_file_location('__main__', __file__)
report = generate_module_report()
print(report)

Finally, god

4. Inspect the loader attribute of each entry in the sys.modules dict. Can you find an attribute of loader which reliably tells you if the loader is source, builtin, or extension?

From the module-inspector script, we can get lines for inspecting the loader attribute and make slight changes to have a script like this:

import sys

for module_name, module in sys.modules.items():
    # Check if the module has a loader attribute
    if hasattr(module, '__loader__'):
        loader = module.__loader__
        # Determine the type of loader
        if type(loader).__name__ == 'SourceFileLoader':
            print(f"Module '{module_name}' has a source loader.")
        elif type(loader).__name__ == 'BuiltinImporter':
            print(f"Module '{module_name}' has a built-in loader.")
        elif type(loader).__name__ == 'ExtensionFileLoader':
            print(f"Module '{module_name}' has an extension loader.")

The results vary when I use different python interpreter (here I try the default py3.11 and the anaconda).

loader

anaconda_loader

5. Add import pandas to the top of the module-inspector script and re-run it. Notice that many of the pandas modules are not loaded using the SourceFileLoader. Can you explain why these modules are different?

I guess the reason why these moudles are different is that they are part of a compiled package, specifically including C extension modules such as pandas._libs. In this case, it may use ExtensionFileLoader rather than SourceFileLoader.

Extra credit: use the freeze utility to turn the module-inspector script into a real app.

from cx_Freeze import setup, Executable

executables = [Executable('module-inspector.py')]

setup(
    name='wow',
    version='1.0',
    description='Module-inspector Application',
    executables=executables
)

But the command failed when I run python test11.py build (test11.py is this file's name) and I don't know why. Could you please help to have a look?

warning