Финтех python
Лекция 4
94210-67030
Кто не сделал 2 домашнюю работу?
typing
pep-484
class Message:
...
def some_func(data: dict) -> str:
...
def other_func(message: Message) -> None:
...
На кой оно надо?
Язык то динамический!
Duck typing
Если крякает и плавает
class Long:
def __len__(self):
return 282
assert len(Long()) == 282
def len(obj):
return obj.__len__()
Статический анализ
В момент написания кода
class Message:
...
def some_func(data: dict) -> str:
...
def other_func(message: Message) -> None:
...
Как это работает?
some_func.__annotations__
{
'data': dict,
'return': str
}
other_func.__annotations__
{
'message': __main__.Message,
'return': None
}
И все
- Нет проверок
- Нет ограничений
- Только один дополнительный словарь
Что это нам дает?
def some_func() -> typing.Tuple[str, int]:
return 'a', 1
def other_func() -> typing.List[int]:
return [1, 2, 3, 4]
def add(x: int, y: int) -> int:
return x + y + z
result = add('kek', 'lol') + '!'
error: Name 'z' is not defined
error: Argument 1 to "add" has incompatible type "str"; expected "int"
error: Argument 2 to "add" has incompatible type "str"; expected "int"
error: Unsupported operand types for + ("int" and "str")
mypy example

Подсветка синтаксиса
import typing
class Message(typing.NamedTuple):
id: int
timestamp: datetime.datetime
text: str
from dataclasses import dataclass
@dataclass()
class Message:
id: int
timestamp: datetime.datetime
text: str
"Проблемы"
Self refs
class Some:
def some_method(self) -> Some:
...
Self refs
class Some:
def some_method(self) -> 'Some':
...
Кольцевой импорт
# message.py
from .dialog import Dialog
class Message:
def some_method(self) -> Dialog:
...
# dialog.py
from .message import Message
class Dialog:
def other_method(self) -> Message:
...
Кольцевой импорт
# message.py
from .dialog import Dialog
# message.py
if typing.TYPE_CHECKING:
from .dialog import Dialog
Кольцевой импорт
# message.py
if typing.TYPE_CHECKING:
from .dialog import Dialog
class Message:
def some_method(self) -> Dialog:
...
# dialog.py
if typing.TYPE_CHECKING:
from .message import Message
class Dialog:
def other_method(self) -> Message:
...
Метаклассы

Либы без типов
Stubs
Решение для метаклассов и для либ
typeshed - github.com/python/typeshed
others stubs - github.com/dropbox/sqlalchemy-stubs
class Path(PurePath):
@classmethod
def cwd(cls: Type[_P]) -> _P: ...
def stat(self) -> os.stat_result: ...
def chmod(self, mode: int) -> None: ...
def exists(self) -> bool: ...
def group(self) -> str: ...
def is_dir(self) -> bool: ...
def is_file(self) -> bool: ...
def is_symlink(self) -> bool: ...
def is_socket(self) -> bool: ...
# и т.д
Stubs
Файлы *.pyi
Stub gen
Сеть

Питон в сети
Сеть не надежна
Ваши примеры ненадежной сети
Клиент
Сеть
Сервис
t
t
t
Типичное решение
timeout'ы
Еще больше вопросов
- Получил ли сервис наш запрос или запрос на обратном пути умер
- Какой таймаут выставить
- Каскадные сбои
Главное правило
Всегда выставлять timeout'ы!
Гнезда
sockets
import socket
with socket.socket(
socket.AF_INET,
socket.SOCK_STREAM) as s:
...
TCP
import socket
with socket.socket() as s:
...
import socket
HOST = '127.0.0.1'
PORT = 65432
with socket.socket() as s:
s.bind((HOST, PORT))
s.listen()
conn, addr = s.accept()
with conn:
print('Connected by', addr)
while True:
data = conn.recv(1024)
if not data:
break
conn.sendall(data)
Echo Server
- socket()
- bind()
- listen()
- accept()
- connect()
- connect_ex()
- send()
- recv()
- close()
Socket API

import socket
HOST = '127.0.0.1'
PORT = 65432
with socket.socket() as s:
s.connect((HOST, PORT))
s.sendall(b'Hello')
data = s.recv(1024)
print('Received', repr(data))
Echo Client
Типы сокетов
- AF_INET
- AF_UNIX
Типы потоков
- SOCK_STREAM
- SOCK_DGRAM
- SOCK_RAW
unix socket
unix:///some_file
sock raw
>>> from scapy.all import *
>>> conf.verb = 0
>>> p = IP(dst="tinkoff.ru") / TCP()
>>> r = sr1(p)
>>> print(r.summary())
IP / TCP 10.218.84.1:http >
172.18.57.5:ftp_data SA / Padding
Scapy
setsockopt
Позволяет настраивать свойства и поведение
- IP_FREEBIND
- IP_PKTINFO
- SO_REUSEADDR
socket.setsockopt(socket.SOL_SOCKET,
socket.SO_REUSEADDR, 1)
0.0.0.0
import socketserver
import socketserver
class MyTCPHandler(socketserver.BaseRequestHandler):
def handle(self):
self.data = self.request.recv(1024).strip()
print('Connected by', self.client_address[0])
self.request.sendall(self.data)
if __name__ == "__main__":
HOST, PORT = "127.0.0.1", 65432
with socketserver.TCPServer((HOST, PORT),
MyTCPHandler) as server:
server.serve_forever()
Поддерживает потоки
import struct
Unpack bytes to values
In [1]: struct.pack('II', 459, 2123)
Out[1]: b'\xcb\x01\x00\x00K\x08\x00\x00'
In [2]: b = (b'\xff\xff\xff\xff'
...: b'\xff\xff\xff\xff')
In [3]: struct.unpack('II', b)
Out[3]: (4294967295, 4294967295)
In [4]: struct.unpack('ff', b)
Out[4]: (nan, nan)
import ctypes
class UserPassword(ctypes.Structure):
_fields_ = [
('id', ctypes.c_uint32),
('hash', ctypes.c_uint32)
]
@classmethod
def from_id_and_password(cls, user_id, password):
pwd = hashlib.sha256(password.encode()).digest()
record = cls()
record.id = user_id
record.hash = ctypes.c_uint32.from_buffer_copy(pwd)
return record
import ctypes
Pack and unpack bytes to values
HTTP
curl http://tinkoff.ru
import socket
ip = socket.gethostbyname('tinkoff.ru')
http = b"""\
GET / HTTP/1.1
Host: tinkoff.ru
Connection: close\n
"""
with socket.socket() as s:
s.connect((ip, 80))
s.sendall(http)
response = s.recv(1024)
print(response.decode('utf8'))
HTTP/1.1 301 Moved Permanently
Content-length: 0
Location: https://tinkoff.ru/
Connection: close
Результат
curl https://tinkoff.ru
Типы http запросов
- head
- get
- post
- put
- delete
- ...
- PROFIT
Статусы ответов
In [1]: from http import HTTPStatus
In [2]: HTTPStatus.OK
Out[2]: <HTTPStatus.OK: 200>
In [3]: HTTPStatus.NOT_FOUND
Out[3]: <HTTPStatus.NOT_FOUND: 404>
In [4]: HTTPStatus.BAD_GATEWAY == 502
Out[4]: True
In [5]: HTTPStatus(404)
Out[5]: <HTTPStatus.NOT_FOUND: 404>
import http
import http.server
python -m http.server 8000
from http.server import (BaseHTTPRequestHandler,
HTTPServer)
class SomeServer(BaseHTTPRequestHandler):
def do_GET(self):
self.send_response(200)
self.send_header('Content-type', 'text/html')
self.end_headers()
self.wfile.write(b'Hello')
return
if __name__ == '__main__':
server_address = ('127.0.0.1', 8081)
httpd = HTTPServer(server_address, SomeServer)
httpd.serve_forever()
HTTP server
Остальные модули http
import urllib.parse
from urllib.parse import urlparse
o = urlparse('http://www.cwi.nl:80/%7Eguido/Python.html')
ParseResult(scheme='http', netloc='www.cwi.nl:80',
path='/%7Eguido/Python.html',
params='', query='', fragment='')
urlparse
from urllib.parse import ParseResult
result = ParseResult('http', 'tinkoff.ru',
'', '', '', '')
result.geturl()
'http://tinkoff.ru'
urljoin
import urllib
import urllib.request
url = 'http://tinkoff.ru'
with urllib.request.urlopen(url) as f:
print(f.read()[:10])
b'<!DOCTYPE '
pip install requests
import requests
r = requests.get('http://tinkoff.ru',
allow_redirects=False)
r.content
b''
r.headers
{
'Content-length': '0',
'Location': 'https://tinkoff.ru/'
}
pip install furl
>>> from furl import furl
>>> u = 'http://www.google.com/?one=1&two=2'
>>> f = furl(u)
>>> f.args['three'] = '3'
>>> del f.args['one']
>>> f.url
'http://www.google.com/?two=2&three=3'
pathlib.Path в мире url
Не делайте так
request.get('http://some_host/some_url?args=val')
request.get('http://some_host/some_url',
params={'args': 'val'})
Только так:
CGI
Common Gateway Interface
print("Content-Type: text/html")
print()
Минимальный CGI скрипт
import cgi
python -m http.server --cgi 8000
Минусы?
def simplest_wsgi_app(environ, start_response):
headers = [(b'Content-Type', b'text/plain')]
start_response(b'200 OK', headers)
yield b'Hello, world!'
Minimal WSGI APP
WSGI протокол
- python функция
- два аргумента (environ, start_request)
- environ это заголовки запроса
- start_request это конец заголовков
- результат функции это http response
WSGI server
Gateway
WSGI application \
framework
callable
import wsgiref
with make_server('', 8000, some_app) as server:
server.serve_forever()
uWSGI
uwsgi --http :8080 --wsgi-file some_file.py
- Быстрый
- С багами
- Сложный
- Много прибамбасов
Gunicorn
gunicorn -w 4 some_file:app
- Немного медленней чем uwsgi
- Стабильней
- Проще
- Достаточно гибкий
Домашняя работа
Клиент серверное приложение на сокетах
Python fintech 4
By Denis Kataev
Python fintech 4
- 433