Danil Akhtarov
Programmer
Ахтаров Данил
Проще диагностировать проблемы
Меньше магии
Всегда знаем куда пойти посмотреть
Исходный текст
(test.py)
Байт код
(test.pyc)
Выполнение
(PVM)
Компактное представление
Ограниченный набор команд
Можно сразу интерпретировать без дополнительных шагов
def say_hello():
print("Hello, World!")
say_hello()
$ python -m tokenize hello.py
0,0-0,0: ENCODING 'utf-8'
1,0-1,3: NAME 'def'
1,4-1,13: NAME 'say_hello'
1,13-1,14: OP '('
1,14-1,15: OP ')'
1,15-1,16: OP ':'
1,16-1,17: NEWLINE '\n'
2,0-2,4: INDENT ' '
2,4-2,9: NAME 'print'
2,9-2,10: OP '('
2,10-2,25: STRING '"Hello, World!"'
2,25-2,26: OP ')'
2,26-2,27: NEWLINE '\n'
3,0-3,1: NL '\n'
4,0-4,0: DEDENT ''
4,0-4,9: NAME 'say_hello'
4,9-4,10: OP '('
4,10-4,11: OP ')'
4,11-4,12: NEWLINE '\n'
5,0-5,0: ENDMARKER ''
...
comp_op: '<'|'>'|'=='|'>='|'<='|'<>'|'!='|'in'|'not' 'in'|'is'|'is' 'not'
star_expr: '*' expr
expr: xor_expr ('|' xor_expr)*
xor_expr: and_expr ('^' and_expr)*
and_expr: shift_expr ('&' shift_expr)*
shift_expr: arith_expr (('<<'|'>>') arith_expr)*
arith_expr: term (('+'|'-') term)*
...
import ast
tree = ast.parse('print("Hello world")')
tree
# <_ast.Module at 0x10a3ba0b8>
import ast
node = ast.UnaryOp()
node.op = ast.USub()
node.operand = ast.Constant()
node.operand.value = 5
node.operand.lineno = 0
node.operand.col_offset = 0
node.lineno = 0
node.col_offset = 0
import dis
def foo(y):
x = 1
return x + y
dis.dis(foo)
4 0 LOAD_CONST 1 (1)
2 STORE_FAST 1 (x)
5 4 LOAD_FAST 1 (x)
6 LOAD_FAST 0 (y)
8 BINARY_ADD
10 RETURN_VALUE
def f(num):
if num == 42:
return True
return False
(1)|(2)|(3)|(4)| (5) |(6)| (7)
---|---|---|---|----------------------|---|-------
2| | | 0|LOAD_FAST | 0|(num)
|-->| | 2|LOAD_CONST | 1|(42)
| | | 4|COMPARE_OP | 2|(==)
| | | 6|POP_JUMP_IF_FALSE | 12|
| | | | | |
3| | | 8|LOAD_CONST | 2|(True)
| | | 10|RETURN_VALUE | |
| | | | | |
4| |>> | 12|LOAD_CONST | 3|(False)
| | | 14|RETURN_VALUE | |
// ceval.c
for (;;) {
switch (opcode) {
case TARGET(NOP): {
FAST_DISPATCH();
}
case TARGET(LOAD_FAST): {
...
}
case TARGET(LOAD_CONST): {
PREDICTED(LOAD_CONST);
PyObject *value = GETITEM(consts, oparg);
Py_INCREF(value);
PUSH(value);
FAST_DISPATCH();
}
case TARGET(STORE_FAST): {
PREDICTED(STORE_FAST);
PyObject *value = POP();
SETLOCAL(oparg, value);
FAST_DISPATCH();
}
case TARGET(POP_TOP): {
...
}
case TARGET(ROT_TWO): {
...
}
case TARGET(ROT_THREE): {
...
}
case TARGET(ROT_FOUR): {
...
}
case TARGET(DUP_TOP): {
...
}
case TARGET(DUP_TOP_TWO): {
...
}
case TARGET(UNARY_POSITIVE): {
...
}
case TARGET(UNARY_NEGATIVE): {
...
}
case TARGET(UNARY_NOT): {
...
}
case TARGET(UNARY_INVERT): {
...
}
case TARGET(BINARY_POWER): {
...
}
case TARGET(BINARY_MULTIPLY): {
...
}
case TARGET(BINARY_MATRIX_MULTIPLY): {
...
}
case TARGET(BINARY_TRUE_DIVIDE): {
...
}
case TARGET(BINARY_FLOOR_DIVIDE): {
PyObject *divisor = POP();
PyObject *dividend = TOP();
PyObject *quotient = PyNumber_FloorDivide(dividend, divisor);
Py_DECREF(dividend);
Py_DECREF(divisor);
SET_TOP(quotient);
if (quotient == NULL)
goto error;
DISPATCH();
}
case TARGET(BINARY_MODULO): {
...
}
...
}
}
/* Minimal main program -- everything is loaded from the library */
#include "Python.h"
#include "pycore_pylifecycle.h"
#ifdef MS_WINDOWS
int
wmain(int argc, wchar_t **argv)
{
return Py_Main(argc, argv);
}
#else
int
main(int argc, char **argv)
{
return Py_BytesMain(argc, argv);
}
#endif
// ceval.c
for (;;) {
switch (opcode) {
case TARGET(NOP): {
FAST_DISPATCH();
}
case TARGET(LOAD_FAST): {
...
}
case TARGET(LOAD_CONST): {
PREDICTED(LOAD_CONST);
PyObject *value = GETITEM(consts, oparg);
Py_INCREF(value);
PUSH(value);
FAST_DISPATCH();
}
case TARGET(STORE_FAST): {
PREDICTED(STORE_FAST);
PyObject *value = POP();
SETLOCAL(oparg, value);
FAST_DISPATCH();
}
case TARGET(POP_TOP): {
...
}
case TARGET(ROT_TWO): {
...
}
case TARGET(ROT_THREE): {
...
}
case TARGET(ROT_FOUR): {
...
}
case TARGET(DUP_TOP): {
...
}
case TARGET(DUP_TOP_TWO): {
...
}
case TARGET(UNARY_POSITIVE): {
...
}
case TARGET(UNARY_NEGATIVE): {
...
}
case TARGET(UNARY_NOT): {
...
}
case TARGET(UNARY_INVERT): {
...
}
case TARGET(BINARY_POWER): {
...
}
case TARGET(BINARY_MULTIPLY): {
...
}
case TARGET(BINARY_MATRIX_MULTIPLY): {
...
}
case TARGET(BINARY_TRUE_DIVIDE): {
...
}
case TARGET(BINARY_FLOOR_DIVIDE): {
PyObject *divisor = POP();
PyObject *dividend = TOP();
PyObject *quotient = PyNumber_FloorDivide(dividend, divisor);
Py_DECREF(dividend);
Py_DECREF(divisor);
SET_TOP(quotient);
if (quotient == NULL)
goto error;
DISPATCH();
}
case TARGET(BINARY_MODULO): {
...
}
...
}
}
code, args, kwargs, const, etc.
code, args, kwargs, const, etc.
code, args, kwargs, const, etc.
current frame
Модуль
main
sort
sort_file
import inspect
current_frame = inspect.currentframe()
current_frame
# <frame at 0x1068357a8, file '<...>', line 1, code <module>>
current_frame.f_back
# <frame at 0x7fa91b874df8, file './.py', line 2981, code run_code>
>>> import requests
>>> requests.get(42)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/usr/lib/python3/dist-packages/requests/api.py", line 55, in get
return request('get', url, **kwargs)
File "/usr/lib/python3/dist-packages/requests/api.py", line 44, in request
return session.request(method=method, url=url, **kwargs)
File "/usr/lib/python3/dist-packages/requests/sessions.py", line 421, in request
prep = self.prepare_request(req)
File "/usr/lib/python3/dist-packages/requests/sessions.py", line 359, in prepare_request
hooks=merge_hooks(request.hooks, self.hooks),
File "/usr/lib/python3/dist-packages/requests/models.py", line 287, in prepare
self.prepare_url(url, params)
File "/usr/lib/python3/dist-packages/requests/models.py", line 338, in prepare_url
"Perhaps you meant http://{0}?".format(url))
requests.exceptions.MissingSchema: Invalid URL '42': No schema supplied. Perhaps you meant http://42?
// ceval.c
for (;;) {
switch (opcode) {
case TARGET(NOP): {
FAST_DISPATCH();
}
case TARGET(LOAD_FAST): {
...
}
case TARGET(LOAD_CONST): {
PREDICTED(LOAD_CONST);
PyObject *value = GETITEM(consts, oparg);
Py_INCREF(value);
PUSH(value);
FAST_DISPATCH();
}
case TARGET(STORE_FAST): {
PREDICTED(STORE_FAST);
PyObject *value = POP();
SETLOCAL(oparg, value);
FAST_DISPATCH();
}
case TARGET(POP_TOP): {
...
}
case TARGET(ROT_TWO): {
...
}
case TARGET(ROT_THREE): {
...
}
case TARGET(ROT_FOUR): {
...
}
case TARGET(DUP_TOP): {
...
}
case TARGET(DUP_TOP_TWO): {
...
}
case TARGET(UNARY_POSITIVE): {
...
}
case TARGET(UNARY_NEGATIVE): {
...
}
case TARGET(UNARY_NOT): {
...
}
case TARGET(UNARY_INVERT): {
...
}
case TARGET(BINARY_POWER): {
...
}
case TARGET(BINARY_MULTIPLY): {
...
}
case TARGET(BINARY_MATRIX_MULTIPLY): {
...
}
case TARGET(BINARY_TRUE_DIVIDE): {
...
}
case TARGET(BINARY_FLOOR_DIVIDE): {
PyObject *divisor = POP();
PyObject *dividend = TOP();
PyObject *quotient = PyNumber_FloorDivide(dividend, divisor);
Py_DECREF(dividend);
Py_DECREF(divisor);
SET_TOP(quotient);
if (quotient == NULL)
goto error;
DISPATCH();
}
case TARGET(BINARY_MODULO): {
...
}
...
}
}
А что с print-ом не так?
import logging
logger = logging.getLogger(__name__)
def foo():
logger.info('Some log text')
import logging
logging.basicConfig(filename="file.log", level=logging.INFO)
dictLogConfig = {
"version":1,
"handlers":{
"fileHandler":{
"class":"logging.FileHandler",
"formatter":"myFormatter",
"filename":"config2.log"
}
},
"loggers":{
"exampleApp":{
"handlers":["fileHandler"],
"level":"INFO",
}
},
"formatters":{
"myFormatter":{
"format":"%(asctime)s - %(name)s - %(levelname)s - %(message)s"
}
}
}
logging.config.dictConfig(dictLogConfig)
[loggers]
keys=root,exampleApp
[handlers]
keys=fileHandler, consoleHandler
[formatters]
keys=myFormatter
[logger_root]
level=CRITICAL
handlers=consoleHandler
[logger_exampleApp]
level=INFO
handlers=fileHandler
qualname=exampleApp
[handler_consoleHandler]
class=StreamHandler
level=DEBUG
formatter=myFormatter
args=(sys.stdout,)
[handler_fileHandler]
class=FileHandler
formatter=myFormatter
args=("config.log",)
[formatter_myFormatter]
format=%(asctime)s - %(name)s - %(levelname)s - %(message)s
datefmt=
import logging
logging.config.fileConfig('logging.conf')
import logging
logging.basicConfig(filename="filname.log", level=logging.INFO)
logger = logging.getLogger(__name__)
try:
raise RuntimeError
except RuntimeError:
logger.exception("Error!")
FORMAT = '%(asctime)-15s %(clientip)s %(user)-8s %(message)s'
logging.basicConfig(format=FORMAT)
d = {'clientip': '192.168.0.1', 'user': 'fbloggs'}
logging.warning('Protocol problem: %s', 'connection reset', extra=d)
2006-02-08 22:20:02,165 192.168.0.1 fbloggs Protocol problem: connection reset
logger = logging.getLogger(__name__)
logger.setLevel(logging.INFO)
# create the logging file handler
fh = logging.FileHandler("filename.log")
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
fh.setFormatter(formatter)
# add handler to logger object
logger.addHandler(fh)
logger.info("Program started")
Worker, Consumer, REST API Server, Connector, ...
Host 1
Host 2
Host 3
Монолит
Можно обмениваться сообщениями в комнатах (PtP)
Authorization
Consumer 1
Consumer 2
Producer 1
Producer 2
Settings
Messages
Producer 3
Использую Basic Auth и не парюсь!
Access token - 15 минут (много раз)
Refresh token - 1 неделя (один раз)
json web token
{
"alg": "HS256",
"typ": "JWT"
}
{
"sub": "1234567890",
"name": "John Doe",
"admin": true
}
iss, sub, aud, exp, nbf, jti, iat
>>> import jwt
>>> encoded_jwt = jwt.encode({'some': 'payload'}, 'secret', algorithm='HS256')
>>> encoded_jwt
'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.
eyJzb21lIjoicGF5bG9hZCJ9.4twFt5NiznN84AWoo1d7KO1T_yoc0Z6XOpOVswacPZg'
>>> jwt.decode(encoded_jwt, 'secret', algorithms=['HS256'])
{'some': 'payload'}
GET /metrics
python_threads_total 10
...
app_version_info 1.0
app_connections_total{role="admin"} 10
app_connections_total{role="user"} 110
...
Переменные окружения
from pydantic import BaseSettings
class Config(BaseSettings):
MAX_SIZE: int = 1024
KEY: str
class Config:
env_prefix = 'MY_APP_'
config = Config()
Двенадцать факторов
By Danil Akhtarov
Как устроен Python интерпретатор. Микросервисы, архитектура построения web приложений.