pep-484
class Message:
...
def some_func(data: dict) -> str:
...
def other_func(message: Message) -> None:
...
Язык то динамический!
Если крякает и плавает
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")
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
class Some:
def some_method(self) -> Some:
...
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:
...
Решение для метаклассов и для либ
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: ...
# и т.д
Файлы *.pyi
Клиент
Сеть
Сервис
t
t
t
timeout'ы
Всегда выставлять timeout'ы!
sockets
import socket
with socket.socket(
socket.AF_INET,
socket.SOCK_STREAM) as s:
...
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)
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))
unix:///some_file
>>> 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
Позволяет настраивать свойства и поведение
socket.setsockopt(socket.SOL_SOCKET,
socket.SO_REUSEADDR, 1)
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()
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
Pack and unpack bytes to values
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
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>
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()
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='')
from urllib.parse import ParseResult
result = ParseResult('http', 'tinkoff.ru',
'', '', '', '')
result.geturl()
'http://tinkoff.ru'
url = 'http://tinkoff.ru'
with urllib.request.urlopen(url) as f:
print(f.read()[:10])
b'<!DOCTYPE '
import requests
r = requests.get('http://tinkoff.ru',
allow_redirects=False)
r.content
b''
r.headers
{
'Content-length': '0',
'Location': 'https://tinkoff.ru/'
}
>>> 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'})
Common Gateway Interface
print("Content-Type: text/html")
print()
def simplest_wsgi_app(environ, start_response):
headers = [(b'Content-Type', b'text/plain')]
start_response(b'200 OK', headers)
yield b'Hello, world!'
WSGI server
Gateway
WSGI application \
framework
callable
with make_server('', 8000, some_app) as server:
server.serve_forever()
uwsgi --http :8080 --wsgi-file some_file.py
gunicorn -w 4 some_file:app