Slide 1

Slide 1 text

Import Deep Dive Petr Viktorin @encukou [email protected] encukou.cz EuroPython 2015

Slide 2

Slide 2 text

import random

Slide 3

Slide 3 text

Prior art David Beazley — Modules and Packages: Live and Let Die! http://pyvideo.org/video/3387

Slide 4

Slide 4 text

Photo © Paul Hermans, https://en.wikipedia.org/wiki/File:Tunnelaquarium_14-05-2009_15-54-09.JPG

Slide 5

Slide 5 text

import random

Slide 6

Slide 6 text

import random random = __import__('random')

Slide 7

Slide 7 text

import random random = __import__('random') from ..spam import foo

Slide 8

Slide 8 text

import random random = __import__('random') from ..spam import foo foo = __import__('spam', globals(), locals(), ['foo'], 2).foo https://docs.python.org/3/library/functions.html#__import__

Slide 9

Slide 9 text

import random random = __import__('random')

Slide 10

Slide 10 text

import random random = __import__('random') The Import Machinery

Slide 11

Slide 11 text

import random random = __import__('random') The Import Machinery importlib.import_module('random')

Slide 12

Slide 12 text

import random The Import Machinery importlib.import_module('random')

Slide 13

Slide 13 text

The Import Machinery Photo © Les Chatfield, https://www.flickr.com/photos/elsie/8229790

Slide 14

Slide 14 text

The Import Machinery Photo © Les Chatfield, https://www.flickr.com/photos/elsie/8229790 sans: • Locking • Caching • Error handling • Pythons older than 3.4 • Backwards compatibility shims

Slide 15

Slide 15 text

importlib.import_module('random')

Slide 16

Slide 16 text

import_module(name): sys.modules[name]? return it spec = find_spec(name, path) load(spec) module = sys.modules[name] return module >>> import random >>> old = random >>> import random >>> random is old True

Slide 17

Slide 17 text

import_module(name): sys.modules[name]? return it spec = find_spec(name, path) load(spec) module = sys.modules[name] return module >>> import random >>> old = random >>> import random >>> random is old True

Slide 18

Slide 18 text

import_module(name): sys.modules[name]? return it spec = find_spec(name, path) load(spec) module = sys.modules[name] return module >>> import random >>> old = random >>> import random >>> random is old True

Slide 19

Slide 19 text

import_module(name): sys.modules[name]? return it spec = find_spec(name, path) load(spec) module = sys.modules[name] return module >>> import random >>> old = random >>> del sys.modules['random'] >>> import random >>> random is old False >>> (random.sample is ... old.sample) False >>> sys.modules['impostor'] = 'Not a module, is it?' >>> import impostor >>> impostor[:12] 'Not a module'

Slide 20

Slide 20 text

import_module(name): sys.modules[name]? return it spec = find_spec(name, path) load(spec) module = sys.modules[name] return module >>> import random >>> old = random >>> del sys.modules['random'] >>> import random >>> random is old False >>> (random.sample is ... old.sample) False >>> sys.modules['impostor'] = 'Not a module, is it?' >>> import impostor >>> impostor[:12] 'Not a module'

Slide 21

Slide 21 text

import_module(name): sys.modules[name]? return it spec = find_spec(name, path) load(spec) module = sys.modules[name] return module >>> sys.path ['', '/usr/lib64/python34.zip', '/usr/lib64/python3.4', ...] >>> importlib.util.find_spec('antigravity') ModuleSpec(name='antigravity', loader=<_frozen_importlib.SourceFileLoader ...>, origin='/usr/lib64/python3.4/antigravity.py') >>> import io >>> io.__spec__ ModuleSpec(name='io', loader=<_frozen_importlib.SourceFileLoader ...>, origin='/usr/lib64/python3.4/io.py')

Slide 22

Slide 22 text

import_module(name): sys.modules[name]? return it spec = find_spec(name, path) load(spec) module = sys.modules[name] return module >>> sys.path ['', '/usr/lib64/python34.zip', '/usr/lib64/python3.4', ...] >>> importlib.util.find_spec('antigravity') ModuleSpec(name='antigravity', loader=<_frozen_importlib.SourceFileLoader ...>, origin='/usr/lib64/python3.4/antigravity.py') >>> import io >>> io.__spec__ ModuleSpec(name='io', loader=<_frozen_importlib.SourceFileLoader ...>, origin='/usr/lib64/python3.4/io.py')

Slide 23

Slide 23 text

import_module(name): sys.modules[name]? return it spec = find_spec(name, path) load(spec) module = sys.modules[name] return module >>> sys.path ['', '/usr/lib64/python34.zip', '/usr/lib64/python3.4', ...] >>> importlib.util.find_spec('antigravity') ModuleSpec(name='antigravity', loader=<_frozen_importlib.SourceFileLoader ...>, origin='/usr/lib64/python3.4/antigravity.py') >>> import io >>> io.__spec__ ModuleSpec(name='io', loader=<_frozen_importlib.SourceFileLoader ...>, origin='/usr/lib64/python3.4/io.py')

Slide 24

Slide 24 text

import_module(name): sys.modules[name]? return it spec = find_spec(name, path) load(spec) module = sys.modules[name] return module >>> sys.path ['', '/usr/lib64/python34.zip', '/usr/lib64/python3.4', ...] >>> importlib.util.find_spec('antigravity') ModuleSpec(name='antigravity', loader=<_frozen_importlib.SourceFileLoader ...>, origin='/usr/lib64/python3.4/antigravity.py') >>> import io >>> io.__spec__ ModuleSpec(name='io', loader=<_frozen_importlib.SourceFileLoader ...>, origin='/usr/lib64/python3.4/io.py')

Slide 25

Slide 25 text

import_module(name): sys.modules[name]? return it spec = find_spec(name, path) load(spec) module = sys.modules[name] return module • Put module in sys.modules • Initialize the module

Slide 26

Slide 26 text

import_module(name): sys.modules[name]? return it spec = find_spec(name, path) load(spec) module = sys.modules[name] return module foo.py: import bar bar.do_bar() def do_foo(): print('Fooing!') bar.py: import foo foo.do_foo() def do_bar(): print('Barring!') • Put module in sys.modules • Initialize the module

Slide 27

Slide 27 text

import_module(name): sys.modules[name]? return it spec = find_spec(name, path) load(spec) module = sys.modules[name] return module foo.py: import bar bar.do_bar() def do_foo(): print('Fooing!') bar.py: import foo foo.do_foo() def do_bar(): print('Barring!') • Put module in sys.modules • Initialize the module

Slide 28

Slide 28 text

import_module(name): sys.modules[name]? return it spec = find_spec(name, path) load(spec) module = sys.modules[name] return module foo.py: import bar bar.do_bar() def do_foo(): print('Fooing!') bar.py: import foo foo.do_foo() def do_bar(): print('Barring!') • Put module in sys.modules • Initialize the module

Slide 29

Slide 29 text

import_module(name): sys.modules[name]? return it spec = find_spec(name, path) load(spec) module = sys.modules[name] return module foo.py: import bar bar.do_bar() def do_foo(): print('Fooing!') bar.py: import foo foo.do_foo() def do_bar(): print('Barring!') • Put module in sys.modules • Initialize the module

Slide 30

Slide 30 text

import_module(name): sys.modules[name]? return it spec = find_spec(name, path) load(spec) module = sys.modules[name] return module foo.py: import bar bar.do_bar() def do_foo(): print('Fooing!') bar.py: import foo foo.do_foo() def do_bar(): print('Barring!') • Put module in sys.modules • Initialize the module

Slide 31

Slide 31 text

import_module(name): sys.modules[name]? return it spec = find_spec(name, path) load(spec) module = sys.modules[name] return module

Slide 32

Slide 32 text

import_module(name): sys.modules[name]? return it spec = find_spec(name, path) load(spec) module = sys.modules[name] return module Filename Module name random.py random urllib/ __init__.py urllib parse.py urllib.parse request.py urllib.request response.py urllib.response Top-level module Package Parent of the below Submodule

Slide 33

Slide 33 text

import_module(name): sys.modules[name]? return it spec = find_spec(name, path) load(spec) module = sys.modules[name] return module Filename Module name random.py random urllib/ __init__.py urllib parse.py urllib.parse request.py urllib.request response.py urllib.response Top-level module Package Parent of the below Submodule

Slide 34

Slide 34 text

import_module(name): sys.modules[name]? return it spec = find_spec(name, path) load(spec) module = sys.modules[name] return module import urllib.parse >>> urllib.__path__ ['/usr/lib64/python3.4/urllib'] Filename Module name random.py random urllib/ __init__.py urllib parse.py urllib.parse request.py urllib.request response.py urllib.response Top-level module Package Parent of the below Submodule

Slide 35

Slide 35 text

import_module(name): sys.modules[name]? return it submodules: load parent module sys.modules[name]? return it spec = find_spec(name, path) load(spec) module = sys.modules[name] submodules: set module as attribute of parent return module import urllib.parse >>> urllib.__path__ ['/usr/lib64/python3.4/urllib'] Filename Module name random.py random urllib/ __init__.py urllib parse.py urllib.parse request.py urllib.request response.py urllib.response Top-level module Package Parent of the below Submodule

Slide 36

Slide 36 text

import_module(name): sys.modules[name]? return it submodules: load parent module sys.modules[name]? return it spec = find_spec(name, path) load(spec) module = sys.modules[name] submodules: set module as attribute of parent return module • Put module in sys.modules • Initialize the module

Slide 37

Slide 37 text

import_module(name): sys.modules[name]? return it submodules: load parent module sys.modules[name]? return it spec = find_spec(name, path) load(spec) module = sys.modules[name] submodules: set module as attribute of parent return module foo/__init__.py: from foo import main from foo import consts foo/main.py: import foo use(foo.consts.value) foo/consts.py: value = 42 • Put module in sys.modules • Initialize the module

Slide 38

Slide 38 text

import_module(name): sys.modules[name]? return it submodules: load parent module sys.modules[name]? return it spec = find_spec(name, path) load(spec) module = sys.modules[name] submodules: set module as attribute of parent return module foo/__init__.py: from foo import main from foo import consts foo/main.py: import foo use(foo.consts.value) foo/consts.py: value = 42 • Put module in sys.modules • Initialize the module

Slide 39

Slide 39 text

import_module(name): sys.modules[name]? return it submodules: load parent module sys.modules[name]? return it spec = find_spec(name, path) load(spec) module = sys.modules[name] submodules: set module as attribute of parent return module foo/__init__.py: from foo import main from foo import consts foo/main.py: import foo use(foo.consts.value) foo/consts.py: value = 42 • Put module in sys.modules • Initialize the module

Slide 40

Slide 40 text

import_module(name): sys.modules[name]? return it submodules: load parent module sys.modules[name]? return it spec = find_spec(name, path) load(spec) module = sys.modules[name] submodules: set module as attribute of parent return module foo/__init__.py: from foo import main from foo import consts foo/main.py: import foo use(foo.consts.value) foo/consts.py: value = 42 • Put module in sys.modules • Initialize the module

Slide 41

Slide 41 text

import_module(name): sys.modules[name]? return it submodules: load parent module sys.modules[name]? return it spec = find_spec(name, path) load(spec) module = sys.modules[name] submodules: set module as attribute of parent return module foo/__init__.py: from foo import main from foo import consts foo/main.py: import foo use(foo.consts.value) foo/consts.py: value = 42 • Put module in sys.modules • Initialize the module

Slide 42

Slide 42 text

import_module(name): sys.modules[name]? return it submodules: load parent module sys.modules[name]? return it spec = find_spec(name, path) load(spec) module = sys.modules[name] submodules: set module as attribute of parent return module foo/__init__.py: from foo import main from foo import consts foo/main.py: import foo use(foo.consts.value) foo/consts.py: value = 42 • Put module in sys.modules • Initialize the module

Slide 43

Slide 43 text

import_module(name): sys.modules[name]? return it submodules: load parent module sys.modules[name]? return it spec = find_spec(name, path) load(spec) module = sys.modules[name] submodules: set module as attribute of parent return module foo/__init__.py: from foo import main from foo import consts foo/main.py: import foo use(foo.consts.value) foo/consts.py: value = 42 • Put module in sys.modules • Initialize the module

Slide 44

Slide 44 text

__init__ should: import from submodules set __all__ nothing else submodules should: not import directly from __init__ not have internal import cycles

Slide 45

Slide 45 text

import_module(name): sys.modules[name]? return it submodules: load parent module sys.modules[name]? return it spec = find_spec(name, path) load(spec) module = sys.modules[name] submodules: set module as attribute of parent return module ?

Slide 46

Slide 46 text

Where's the module? >>> import random >>> random >>> random.__file__ '/usr/lib64/python3.4/random.py' >>> import sys >>> sys >>> sys.__file__ Traceback (most recent call last): File "", line 1, in AttributeError: 'module' object has no attribute '__file__' /usr/bin/python3

Slide 47

Slide 47 text

Where's the module? >>> import random >>> random >>> random.__file__ '/usr/lib64/python3.4/random.py' >>> import sys >>> sys >>> sys.__file__ Traceback (most recent call last): File "", line 1, in AttributeError: 'module' object has no attribute '__file__' /usr/bin/python3

Slide 48

Slide 48 text

Where's the module? >>> import random >>> random >>> random.__file__ '/usr/lib64/python3.4/random.py' >>> import sys >>> sys >>> sys.__file__ Traceback (most recent call last): File "", line 1, in AttributeError: 'module' object has no attribute '__file__' /usr/bin/python3

Slide 49

Slide 49 text

Where's the module? >>> import random >>> random >>> random.__file__ '/usr/lib64/python3.4/random.py' >>> import sys >>> sys >>> sys.__file__ Traceback (most recent call last): File "", line 1, in AttributeError: 'module' object has no attribute '__file__' /usr/bin/python3

Slide 50

Slide 50 text

Where's the module? >>> import random >>> random >>> random.__file__ '/usr/lib64/python3.4/random.py' >>> import sys >>> sys >>> sys.__file__ Traceback (most recent call last): File "", line 1, in AttributeError: 'module' object has no attribute '__file__' /usr/bin/python3

Slide 51

Slide 51 text

Where's the module? >>> import random >>> random >>> random.__file__ '/usr/lib64/python3.4/random.py' >>> import sys >>> sys >>> sys.__file__ Traceback (most recent call last): File "", line 1, in AttributeError: 'module' object has no attribute '__file__' /usr/bin/python3

Slide 52

Slide 52 text

Where's the module? >>> import random >>> random >>> random.__file__ '/usr/lib64/python3.4/random.py' >>> import sys >>> sys >>> sys.__file__ Traceback (most recent call last): File "", line 1, in AttributeError: 'module' object has no attribute '__file__' /usr/bin/python3

Slide 53

Slide 53 text

Photo © Allentchang, https://commons.wikimedia.org/wiki/File:Fish_in_Okinawa_Churaumi_Aquarium.JPG C Python Compiled in Built-in Frozen From File Extension Source sys _frozen_importlib math random

Slide 54

Slide 54 text

Photo © Allentchang, https://commons.wikimedia.org/wiki/File:Fish_in_Okinawa_Churaumi_Aquarium.JPG C Python Compiled in Built-in Frozen From File Extension Source sys _frozen_importlib math random

Slide 55

Slide 55 text

Photo © Allentchang, https://commons.wikimedia.org/wiki/File:Fish_in_Okinawa_Churaumi_Aquarium.JPG C Python Compiled in Built-in Frozen From File Extension Source sys _frozen_importlib math random

Slide 56

Slide 56 text

>>> sys.meta_path [, , ]

Slide 57

Slide 57 text

find_spec(name, path): for finder in sys.meta_path: call its find_spec(name, path) return spec if successful >>> sys.meta_path [, , ]

Slide 58

Slide 58 text

find_spec(name, path): for finder in sys.meta_path: call its find_spec(name, path) return spec if successful >>> sys.meta_path [, , ] >>> sys.path ['', '/usr/lib64/python34.zip', '/usr/lib/python3.4', ...]

Slide 59

Slide 59 text

find_spec(name, path): for finder in sys.meta_path: call its find_spec(name, path) return spec if successful >>> sys.meta_path [, , ] >>> sys.path_hooks [, ] >>> sys.path ['', '/usr/lib64/python34.zip', '/usr/lib/python3.4', ...]

Slide 60

Slide 60 text

find_spec(name, path): for finder in sys.meta_path: call its find_spec(name, path) return spec if successful PathFinder.find_spec(name, path): for directory in path: get sys.path_hooks entry call its find_spec(name) return spec if successful >>> sys.meta_path [, , ] >>> sys.path_hooks [, ] >>> sys.path ['', '/usr/lib64/python34.zip', '/usr/lib/python3.4', ...]

Slide 61

Slide 61 text

find_spec(name, path): for finder in sys.meta_path: call its find_spec(name, path) return spec if successful PathFinder.find_spec(name, path): for directory in path: get sys.path_hooks entry call its find_spec(name) return spec if successful >>> sys.meta_path [, , ] >>> sys.path_hooks [, ] >>> sys.path ['', '/usr/lib64/python34.zip', '/usr/lib/python3.4', ...] random.cpython-34m.so (Extension module) random.abi3.so (Extension module) random.so (Extension module) random.py (Source module) random.pyc (Sourceless module)

Slide 62

Slide 62 text

find_spec(name, path): for finder in sys.meta_path: call its find_spec(name, path) return spec if successful PathFinder.find_spec(name, path): for directory in path: get sys.path_hooks entry call its find_spec(name) return spec if successful ModuleSpec: name random origin /usr/lib64/python3.4/random.py cached /usr/lib64/python3.4/__pycache__/random.cpython-34.pyc loader importlib.machinery.SourceFileLoader loader_state, parent, submodule_search_locations

Slide 63

Slide 63 text

import_module(name): sys.modules[name]? return it submodules: load parent module sys.modules[name]? return it spec = find_spec(name, path) load(spec) module = sys.modules[name] submodules: set module as attribute of parent return module ?

Slide 64

Slide 64 text

load(spec): module = spec.loader.create_module(spec) if module is None: module = types.ModuleType(spec.name) set initial module attributes sys.modules[spec.name] = module spec.loader.exec_module(module, spec)

Slide 65

Slide 65 text

load(spec): module = spec.loader.create_module(spec) if module is None: module = types.ModuleType(spec.name) set initial module attributes sys.modules[spec.name] = module spec.loader.exec_module(module, spec)

Slide 66

Slide 66 text

load(spec): module = spec.loader.create_module(spec) if module is None: module = types.ModuleType(spec.name) set initial module attributes sys.modules[spec.name] = module spec.loader.exec_module(module, spec) spec → __spec__ spec.name → __name__ spec.loader → __loader__ spec.parent → __package__ spec.origin → __file__ spec.cached → __cached__ spec.submodule_search_locations → __path__

Slide 67

Slide 67 text

load(spec): module = spec.loader.create_module(spec) if module is None: module = types.ModuleType(spec.name) set initial module attributes sys.modules[spec.name] = module spec.loader.exec_module(module, spec) spec → __spec__ spec.name → __name__ spec.loader → __loader__ spec.parent → __package__ spec.origin → __file__ spec.cached → __cached__ spec.submodule_search_locations → __path__ >>> import __main__ >>> a = 'hello' >>> __main__.a 'hello' >>> __main__.a = 'world' >>> a 'world'

Slide 68

Slide 68 text

load(spec): module = spec.loader.create_module(spec) if module is None: module = types.ModuleType(spec.name) set initial module attributes sys.modules[spec.name] = module spec.loader.exec_module(module, spec) spec → __spec__ spec.name → __name__ spec.loader → __loader__ spec.parent → __package__ spec.origin → __file__ spec.cached → __cached__ spec.submodule_search_locations → __path__ >>> import __main__ >>> a = 'hello' >>> __main__.a 'hello' >>> __main__.a = 'world' >>> a 'world' if __name__ == '__main__'

Slide 69

Slide 69 text

load(spec): module = spec.loader.create_module(spec) if module is None: module = types.ModuleType(spec.name) set initial module attributes sys.modules[spec.name] = module spec.loader.exec_module(module, spec) spec → __spec__ spec.name → __name__ spec.loader → __loader__ spec.parent → __package__ spec.origin → __file__ spec.cached → __cached__ spec.submodule_search_locations → __path__ >>> import __main__ >>> a = 'hello' >>> __main__.a 'hello' >>> __main__.a = 'world' >>> a 'world' if __name__ == '__main__'

Slide 70

Slide 70 text

load(spec): module = spec.loader.create_module(spec) if module is None: module = types.ModuleType(spec.name) set initial module attributes sys.modules[spec.name] = module spec.loader.exec_module(module, spec) • Put module in sys.modules • Initialize the module

Slide 71

Slide 71 text

get_code(module, spec): if spec.cached exists, and matches origin stats, return it! load source from origin and compile it write bytecode to spec.cached ModuleSpec: name random origin /usr/lib64/python3.4/random.py cached /usr/lib64/python3.4/__pycache__/random.cpython-34.pyc ...

Slide 72

Slide 72 text

get_code(module, spec): if spec.cached exists, and matches origin stats, return it! load source from origin and compile it write bytecode to spec.cached Python 2 somemodule.py somemodule.pyc

Slide 73

Slide 73 text

get_code(module, spec): if spec.cached exists, and matches origin stats, return it! load source from origin and compile it write bytecode to spec.cached Python 2 somemodule.py somemodule.pyc

Slide 74

Slide 74 text

get_code(module, spec): if spec.cached exists, and matches origin stats, return it! load source from origin and compile it write bytecode to spec.cached Python 2 somemodule.py somemodule.pyc Python 3 somemodule.py __pycache__/ somemodule.cpython-34.pyc

Slide 75

Slide 75 text

get_code(module, spec): if spec.cached exists, and matches origin stats, return it! load source from origin and compile it write bytecode to spec.cached Python 2 somemodule.py somemodule.pyc Python 3 somemodule.py __pycache__/ somemodule.cpython-34.pyc Or: somemodule.py somemodule.pyc

Slide 76

Slide 76 text

import_module(name): sys.modules[name]? return it submodules: load parent module sys.modules[name]? return it spec = find_spec(name, path) load(spec) module = sys.modules[name] submodules: set module as attribute of parent return module find_spec(name, path): for finder in sys.meta_path: call its find_spec(name, path) return spec if successful PathFinder.find_spec(name, path): for directory in path: get sys.path_hooks entry call its find_spec(name) return spec if successful load(spec): module = spec.loader.create_module(spec) if module is None: module = types.ModuleType(spec.name) set initial module attributes sys.modules[spec.name] = module spec.loader.exec_module(module, spec) get_code(module, spec): if spec.cached exists, and matches origin stats, return it! load source from origin and compile it write bytecode to spec.cached

Slide 77

Slide 77 text

C Python Bytecode Compiled-in Built-in Frozen From File Extension Sourceless Zip file zip source zip sourceless Source