Skip to main content
ThePythonBook/Assessment

Modules & Packaging Assessment

Test your understanding of Python modules, packages, import mechanisms, namespaces, and the module system. Complete all 10 exercises to earn your certificate.

Progress
800 XP0/10
#1Simulate Module Attribute Access
Write Code

Write a function get_module_attrs(module_name: str) -> dict that takes a standard-library module name (e.g. "math", "json", "os.path") and returns a dictionary with the keys:

  • "name" -- the module's __name__ attribute
  • "has_all" -- a boolean indicating whether the module defines __all__
  • "doc_start" -- the first 20 characters of the module's docstring (or "No docstring" if it is None)
  • Use importlib.import_module to load the module dynamically.

    Loading editor...
    #2Fix the Circular-Style Import Simulation
    Fix the Bug

    The code below simulates two modules that depend on each other by using a shared registry dictionary. However, there is a bug: the greet function crashes because it tries to call a function that hasn't been registered yet.

    Fix the code so that greet("Alice") returns "Hello, ALICE!" by ensuring the registry is populated before it is accessed.

    Loading editor...
    #3Predict the __name__ Behavior
    Predict Output

    Read the code carefully and predict exactly what it will print. Think about how __name__ works and what value it has when code runs as the main script vs. when it is imported.

    Loading editor...
    #4Build an __all__ Filter
    Write Code

    Write a function filter_exports(namespace: dict, all_list: list[str]) -> dict that simulates Python's from module import * behavior.

    Given a namespace dictionary (mapping names to values) and an __all__ list, return a new dictionary containing only the names in __all__ that also exist in the namespace. Skip names that start with _ in the result even if they appear in __all__ (matching real Python behavior for public API filtering).

    Loading editor...
    #5Fix the Namespace Collision
    Fix the Bug

    Two simulated "modules" both define a function called process. The code is supposed to call each one independently, but there is a namespace collision: the second definition silently overwrites the first.

    Fix the code so it prints the correct results from both functions without renaming either function definition.

    Loading editor...
    #6Refactor to Use importlib
    Refactor

    The code below uses eval and string concatenation to dynamically access math functions. Refactor it to use importlib.import_module and getattr instead, which is the proper Pythonic way to do dynamic imports.

    The function call_math_func(func_name, value) should return the result of calling math.<func_name>(value). Keep the same behavior but eliminate the use of eval.

    Loading editor...
    #7Predict Module Caching Behavior
    Predict Output

    Read the code carefully and predict its exact output. Pay attention to how Python caches modules in sys.modules and what happens when you import the same module multiple times.

    Loading editor...
    #8Version Comparison Utility
    Write Code

    Write a function compare_versions(v1: str, v2: str) -> int that compares two semantic version strings (e.g. "1.2.3") and returns:

  • -1 if v1 < v2
  • 0 if v1 == v2
  • 1 if v1 > v2
  • Version strings always have exactly three numeric parts separated by dots. Compare major, then minor, then patch left to right.

    This simulates how package managers compare dependency versions.

    Loading editor...
    #9Fix the Dependency Resolver
    Fix the Bug

    The function resolve_order takes a dictionary mapping package names to their list of dependencies and returns a valid installation order (topological sort). However, it has a bug that causes it to miss some packages or produce an incorrect order.

    Fix the bug so all packages are included and dependencies always come before the packages that need them.

    Loading editor...
    #10Refactor to a Plugin Registry Pattern
    Refactor

    The code below uses a long if/elif chain to dispatch to different "plugin" functions. Refactor it to use a registry dictionary pattern, which is how real Python frameworks (Flask, pytest, Django) manage plugins and extensions.

    Create a PLUGINS dictionary mapping names to functions, a register_plugin function to add entries, and refactor run_plugin to do a simple dictionary lookup. The output must remain the same.

    Loading editor...