The Adventures of a
Python Script!
Dema Abu Adas | @human_dema
Who am i
bit.ly/demastalk
Python Interpreters
Python Interpreters:
Stackless Python
Python Interpreters:
Jython
Python Interpreters:
IronPython
Python Interpreters:
PyPy
Python Interpreters:
CPython
How does CPython work?
def hello_PyGotham():
print(“Hello PyGotham”)
hello_PyGotham()
Tokenizing
0,0-0,0: ENCODING 'utf-8'
1,0-1,3: NAME 'def'
1,4-1,18: NAME 'hello_PyGotham'
1,18-1,19: LPAR '('
1,19-1,20: RPAR ')'
1,20-1,21: COLON ':'
1,21-1,22: NEWLINE '\n'
2,0-2,4: INDENT ' '
2,4-2,9: NAME 'print'
2,9-2,10: LPAR '('
2,10-2,26: STRING '"Hello PyGotham"'
2,26-2,27: RPAR ')'
2,27-2,28: NEWLINE '\n'
3,1-3,2: NL '\n'
4,0-4,0: DEDENT ''
4,0-4,14: NAME 'hello_PyGotham'
4,14-4,15: LPAR '('
4,15-4,16: RPAR ')'
4,16-4,17: NEWLINE '\n'
5,0-5,0: ENDMARKER ''
def hello_PyGotham():
print(“Hello PyGotham”)
hello_PyGotham()
Tokens -> Parser Tree
[257, [269, [295, [263, [1, 'def'], [1, 'hello_PyGotham'], [264, [7, '('], [8, ')']], [11, ':'], [304, [4, ''], [5, ''], [269, [270, [271, [272, [274, [305, [309, [310, [311, [312, [315, [316, [317, [318, [319, [320, [321, [322, [323, [324, [1, 'print']], [326, [7, '('], [334, [335, [305, [309, [310, [311, [312, [315, [316, [317, [318, [319, [320, [321, [322, [323, [324, [3, '"Hello PyGotham!"']]]]]]]]]]]]]]]]]], [8, ')']]]]]]]]]]]]]]]]]]], [4, '']]], [6, '']]]]],[269, [270, [271, [272, [274, [305, [309, [310, [311, [312, [315, [316, [317, [318, [319, [320, [321, [322, [323, [324, [1, 'hello_PyGotham']], [326, [7, '('], [8, ')']]]]]]]]]]]]]]]]]]], [4, '']]], [4, ''], [0, '']]
def hello_PyGotham():
print(“Hello PyGotham”)
hello_PyGotham()
Tokens -> Parser Tree
[257, [269, [295, [263, [1, 'def'], [1, 'hello_PyGotham'], [264, [7, '('], [8, ')']], [11, ':'], [304, [4, ''], [5, ''], [269, [270, [271, [272, [274, [305, [309, [310, [311, [312, [315, [316, [317, [318, [319, [320, [321, [322, [323, [324, [1, 'print']], [326, [7, '('], [334, [335, [305, [309, [310, [311, [312, [315, [316, [317, [318, [319, [320, [321, [322, [323, [324, [3, '"Hello PyGotham!"']]]]]]]]]]]]]]]]]], [8, ')']]]]]]]]]]]]]]]]]]], [4, '']]], [6, '']]]]],[269, [270, [271, [272, [274, [305, [309, [310, [311, [312, [315, [316, [317, [318, [319, [320, [321, [322, [323, [324, [1, 'hello_PyGotham']], [326, [7, '('], [8, ')']]]]]]]]]]]]]]]]]]], [4, '']]], [4, ''], [0, '']]
def hello_PyGotham():
print(“Hello PyGotham”)
hello_PyGotham()
Tokens -> Parser Tree
[257, [269, [295, [263, [1, 'def'], [1, 'hello_PyGotham'], [264, [7, '('], [8, ')']], [11, ':'], [304, [4, ''], [5, ''], [269, [270, [271, [272, [274, [305, [309, [310, [311, [312, [315, [316, [317, [318, [319, [320, [321, [322, [323, [324, [1, 'print']], [326, [7, '('], [334, [335, [305, [309, [310, [311, [312, [315, [316, [317, [318, [319, [320, [321, [322, [323, [324, [3, '"Hello PyGotham!"']]]]]]]]]]]]]]]]]], [8, ')']]]]]]]]]]]]]]]]]]], [4, '']]], [6, '']]]]],[269, [270, [271, [272, [274, [305, [309, [310, [311, [312, [315, [316, [317, [318, [319, [320, [321, [322, [323, [324, [1, 'hello_PyGotham']], [326, [7, '('], [8, ')']]]]]]]]]]]]]]]]]]], [4, '']]], [4, ''], [0, '']]
def hello_PyGotham():
print(“Hello PyGotham”)
hello_PyGotham()
Parser Tree Generation
def hello_PyGotham():
print(“Hello PyGotham”)
hello_PyGotham()
Parser Tree Generation
def hello_PyGotham():
print(“Hello PyGotham”)
hello_PyGotham()
Parser Tree’s Responsibilities
SyntaxError: invalid syntax
def hello_PyGotham():
print(“Hello PyGotham”)
hello_PyGotham()
Parser Tree -> AST
def hello_PyGotham():
print(“Hello PyGotham”)
hello_PyGotham()
AST Responsibilities
def hello_PyGotham():
print(“Hello PyGotham”)
hello_PyGotham()
TypeError: hello_PyGotham() takes no arguments (1 given)
AST -> Control FLow Graphs
def hello_PyGotham():
print(“Hello PyGotham”)
hello_PyGotham()
def hello():....
hello_PyGothaam()
hello_PyGotham
print(“Hello PyGotham)
hello_PyGotham
calls
calls
Another Example
def fib():
a, b = 0, 1
while True:
yield a
a, b = b, a + b
fib_gen = fib()
for _ in range(10):
next(fib_gen)
Another Example
next
next(fib_gen)
a, b = 0, 1
While True:
yield a
a, b = b, a + b
fib
fib
Def fib():...
Fib_gen = fib()
For _ in range(10):
Control FLow Graphs
CFG -> Bytecode generation!
def hello_PyGotham():
print(“Hello PyGotham”)
hello_PyGotham()
4 0 LOAD_GLOBAL 0 (print)
2 LOAD_CONST 1 ('Hello PyGotham')
4 CALL_FUNCTION 1
6 POP_TOP
8 LOAD_CONST 0 (None)
10 RETURN_VALUE
Final steps!
Back to the Abstract Syntax Tree (AST)
What it is
Why is it useful
Linting Example
Linting Example
Linting Example
Correct Import Example
import os
from os import path
from django.conf import settings
from django.db.models import (
CharField,
Field,
)
Incorrect Import Example
from os import path #should be after import os
import os
from django.conf import settings
from django.db.models import (
Field, #should be after CharField
CharField,
)
Astroid Tree Representation
import astroid
source_code = '''
def hello_PyGotham():
print("Hello PyGotham")
hello_PyGotham()
'''
ast = astroid.parse(source_code)
print(ast)
Astroid Tree Representation
Module(name='',
doc=None,
file='<?>',
path=['<?>'],
package=False,
pure_python=True,
future_imports=set(),
body=[ <FunctionDef.hello_PyGotham l.3 at 0x107234400>,
<Expr l.6 at 0x107234470>])
Pylint boilerplate
class AlphabeticallySortedImports(BaseChecker):
__implements__ = IAstroidChecker
name = 'alphabetically-sorted-imports-checker'
UNSORTED_IMPORT_FROM = 'unsorted-import-from'
DIR_HIGHER = 'higher'
DIR_LOWER = 'lower'
msgs = {
'C5001': ('"%s" in "%s" is in the wrong position. Move it %s.',
UNSORTED_IMPORT_FROM,
'Refer to project rules on wiki'),
}
options = ()
priority = -1
Pylint boilerplate
class AlphabeticallySortedImports(BaseChecker):
__implements__ = IAstroidChecker
name = 'alphabetically-sorted-imports-checker'
UNSORTED_IMPORT_FROM = 'unsorted-import-from'
DIR_HIGHER = 'higher'
DIR_LOWER = 'lower'
msgs = {
'C5001': ('"%s" in "%s" is in the wrong position. Move it %s.',
UNSORTED_IMPORT_FROM,
'Refer to project rules on wiki'),
}
options = ()
priority = -1
Pylint boilerplate
class AlphabeticallySortedImports(BaseChecker):
__implements__ = IAstroidChecker
name = 'alphabetically-sorted-imports-checker'
UNSORTED_IMPORT_FROM = 'unsorted-import-from'
DIR_HIGHER = 'higher'
DIR_LOWER = 'lower'
msgs = {
'C5001': ('"%s" in "%s" is in the wrong position. Move it %s.',
UNSORTED_IMPORT_FROM,
'Refer to project rules on wiki'),
}
options = ()
priority = -1
Pylint boilerplate
class AlphabeticallySortedImports(BaseChecker):
__implements__ = IAstroidChecker
name = 'alphabetically-sorted-imports-checker'
UNSORTED_IMPORT_FROM = 'unsorted-import-from'
DIR_HIGHER = 'higher'
DIR_LOWER = 'lower'
msgs = {
'C5001': ('"%s" in "%s" is in the wrong position. Move it %s.',
UNSORTED_IMPORT_FROM,
'Refer to project rules on wiki'),
}
options = ()
priority = -1
Pylint boilerplate
class AlphabeticallySortedImports(BaseChecker):
__implements__ = IAstroidChecker
name = 'alphabetically-sorted-imports-checker'
UNSORTED_IMPORT_FROM = 'unsorted-import-from'
DIR_HIGHER = 'higher'
DIR_LOWER = 'lower'
msgs = {
'C5001': ('"%s" in "%s" is in the wrong position. Move it %s.',
UNSORTED_IMPORT_FROM,
'Refer to project rules on wiki'),
}
options = ()
priority = -1
Actual implementation!
def visit_importfrom(self, node):
names = [name for name, _alias in node.names]
sorted_names = sorted(names)
for actual_index, name in enumerate(names):
correct_index = sorted_names.index(name)
if correct_index != actual_index:
direction = self.DIR_LOWER if correct_index > actual_index else self.DIR_HIGHER
args = name, node.as_string(), direction
self.add_message(
self.UNSORTED_IMPORT_FROM, node=node, args=args
)
Actual implementation!
def visit_importfrom(self, node):
names = [name for name, _alias in node.names]
sorted_names = sorted(names)
for actual_index, name in enumerate(names):
correct_index = sorted_names.index(name)
if correct_index != actual_index:
direction = self.DIR_LOWER if correct_index > actual_index else self.DIR_HIGHER
args = name, node.as_string(), direction
self.add_message(
self.UNSORTED_IMPORT_FROM, node=node, args=args
)
Actual implementation!
def visit_importfrom(self, node):
names = [name for name, _alias in node.names]
sorted_names = sorted(names)
for actual_index, name in enumerate(names):
correct_index = sorted_names.index(name)
if correct_index != actual_index:
direction = self.DIR_LOWER if correct_index > actual_index else self.DIR_HIGHER
args = name, node.as_string(), direction
self.add_message(
self.UNSORTED_IMPORT_FROM, node=node, args=args
)
Debugging Example
Birdseye!
How birdseye works!
@eye
def foo():
x = 1
y = 2
z = 3 * (x + y)
if (x > y):
y += 30
How birdseye works!
How birdseye works!
Cool things about birdseye
Thank you!