Update page 'Day 9 homework - Sloetjes'
@@ -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()
|
||||||
|
```
|
||||||
Reference in New Issue
Block a user