From 590ebea1bafa7831d75ee68283a6bde0023aebcb Mon Sep 17 00:00:00 2001 From: "xuanyi.wang" Date: Wed, 19 Apr 2023 00:52:53 +0200 Subject: [PATCH] Update page 'Day 9 homework - Sloetjes' --- Day 9 homework - Sloetjes.md | 129 +++++++++++++++++++++++++++++++++++ 1 file changed, 129 insertions(+) create mode 100644 Day 9 homework - Sloetjes.md diff --git a/Day 9 homework - Sloetjes.md b/Day 9 homework - Sloetjes.md new file mode 100644 index 0000000..6d714eb --- /dev/null +++ b/Day 9 homework - Sloetjes.md @@ -0,0 +1,129 @@ +1. Use `importlib.util.find_spec` to create a function that takes a Python module or package name, and returns the total number of bytes in the cached `.pyc` file, or the total number of bytes in the `.py` file if there is no cached bytecode. You can use the os.stat command to get the size of a file. + +``` +import importlib.util +import os + +def get_module_size(module_name): + """Returns the size of a module's bytecode file or source file.""" + spec = importlib.util.find_spec(module_name) + if spec is None: + return 0 + if spec.cached_bytecode: + return os.stat(spec.cached_bytecode).st_size + elif spec.origin: + return os.stat(spec.origin).st_size + return 0 +``` + +2. Create another function that uses the last function to see how many bytes are in all the modules listed in sys.modules. +``` +import sys + +def get_total_module_size(): + """Returns the total size of all loaded modules.""" + total_size = 0 + for module_name in sys.modules: + total_size += get_module_size(module_name) + return total_size +``` + +3. Run your Python interpreter like you would normally, using Jupyter, and run the last function. How many bytes are being used? + +4. Now, run the same function, but start the Python interpreter in Powershell (Windows), or the regular shell (MacOS/Linux) with the `-E` flag, which ignores environment variables and should only load the modules that came with the original build of the Python interpreter. How many bytes are being used now? + +5. With only the `sys` module loaded, look at the `sys.modules` dict. What is the type of the value of the `os` key? + +6. Import the os module. What happens when you type `type(os)`? + +7. Restart the Python interpreter. Can you figure out how to call `os.getlogin()` without saying `import os`? +``` +login = __import__('os').getlogin() +print(login) +``` +8. If you can, explain why this works and quote the relevant sections of section 5 in the language reference. + +9. Using the functions you created above and `sys.modules`, create a module designed to be run from the command line (`python -m myscript.py`) that generates a report summarizing the module environment, and break down the percentages of bytecode modules, DSO modules (these have `__file__` end in `.so` or `.dll`), and normal python code. + +``` +import sys +import os.path + +# Classify each module based on its __file__ attribute +bytecode_modules = [] +dso_modules = [] +source_modules = [] +for module_name, module in sys.modules.items(): + if module is None or not hasattr(module, '__file__'): + continue + filename = module.__file__ + if filename.endswith('.pyc'): + bytecode_modules.append(module_name) + elif filename.endswith(('.so', '.dll')): + dso_modules.append(module_name) + else: + source_modules.append(module_name) + +# Calculate the percentages of each module type +total_modules = len(sys.modules) +bytecode_percent = len(bytecode_modules +``` + +10. Add a feature to the script which looks at `__file__` for each module, trims the filename to get the directory name, then prints out a histogram of how many modules are in each directory name. If you can't find a text histogram output library in anaconda's built-in modules, just print out the `*` character for each module. +``` +import importlib.util +import os +import sys + +def get_module_size(module_name): + spec = importlib.util.find_spec(module_name) + if spec is None: + return 0 + elif spec.origin.endswith(".py"): + filename = spec.origin + elif spec.cached: + filename = spec.cached + else: + return 0 + return os.stat(filename).st_size + +def get_module_sizes(): + sizes = {} + for module_name in sys.modules: + sizes[module_name] = get_module_size(module_name) + return sizes + +def summarize_modules(): + total_size = 0 + bytecode_size = 0 + dso_size = 0 + python_size = 0 + module_counts = {} + for module_name, size in get_module_sizes().items(): + if size == 0: + continue + total_size += size + if module_name.endswith("pyc"): + bytecode_size += size + elif hasattr(sys.modules[module_name], "__file__"): + file_name = sys.modules[module_name].__file__ + if file_name.endswith((".so", ".dll")): + dso_size += size + elif file_name.endswith(".py"): + python_size += size + + directory = os.path.dirname(os.path.abspath(file_name)) + module_counts[directory] = module_counts.get(directory, 0) + 1 + + print(f"Total module size: {total_size} bytes") + print(f"Bytecode modules: {bytecode_size/total_size*100:.2f}%") + print(f"DSO modules: {dso_size/total_size*100:.2f}%") + print(f"Python modules: {python_size/total_size*100:.2f}%") + + print("Module directory histogram:") + for directory, count in module_counts.items(): + print(f"{directory}: {'*' * count}") + +if __name__ == "__main__": + summarize_modules() +``` \ No newline at end of file