Взаимодействие на основе патчей. Работа с сетью

Ещё про работу с историей

Статья на Хабре про стратегии git merge

Раздельное добавление ханков

Работа с патчами и наборами патчей

Немного о формате

Патчи и Git:

Патч или набор с точки зрения GIT — это сериализация коммитов, превращение их в пригодный для передачи формат.

BTW: difflib

Простейший сетевой сервер

Простейший низкоуровневый сервер с помощью socket

   1 import socket
   2 import sys
   3 
   4 host = "localhost" if len(sys.argv) < 2 else sys.argv[1]
   5 port = 1337 if len(sys.argv) < 3 else int(sys.argv[2])
   6 with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
   7     s.bind((host, port))
   8     s.listen()
   9     conn, addr = s.accept()
  10     with conn:
  11         print('Connected by', addr)
  12         while data := conn.recv(1024):
  13             conn.sendall(data.swapcase())

   1 import socket
   2 import sys
   3 import multiprocessing
   4 
   5 def serve(conn, addr):
   6     with conn:
   7       print('Connected by', addr)
   8       while data := conn.recv(1024):
   9             conn.sendall(data.swapcase())
  10 
  11 host = "localhost" if len(sys.argv) < 2 else sys.argv[1]
  12 port = 1337 if len(sys.argv) < 3 else int(sys.argv[2])
  13 with socket.create_server((host, port)) as s:
  14     s.listen()
  15     while True:
  16         conn, addr = s.accept()
  17         multiprocessing.Process(target=serve, args=(conn, addr)).start()

Тупой аналог netcat с помощью socket:

   1 import sys
   2 import socket
   3 
   4 host = "localhost" if len(sys.argv) < 2 else sys.argv[1]
   5 port = 1337 if len(sys.argv) < 3 else int(sys.argv[2])
   6 with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
   7     s.connect((host, port))
   8     while msg := sys.stdin.buffer.readline():
   9         s.sendall(msg)
  10         print(s.recv(1024).rstrip().decode())

Asyncio Streams

Недостаток threading — необходимость следить за thread safe и синхронизация по доступу к данным; недостаток multiprocessing — ещё более замороченная

Выход: asyncio. Посмотрим в примеры. В частности, echo-сервер:

   1 import asyncio
   2 
   3 async def echo(reader, writer):
   4     while data := await reader.readline():
   5         writer.write(data.swapcase())
   6     writer.close()
   7     await writer.wait_closed()
   8 
   9 async def main():
  10     server = await asyncio.start_server(echo, '0.0.0.0', 1337)
  11     async with server:
  12         await server.serve_forever()
  13 
  14 asyncio.run(main())

Используем Streams для написания «общего чата».

   1 #!/usr/bin/env python3
   2 import asyncio
   3 
   4 clients = {}
   5 
   6 async def chat(reader, writer):
   7     me = "{}:{}".format(*writer.get_extra_info('peername'))
   8     print(me)
   9     clients[me] = asyncio.Queue()
  10     send = asyncio.create_task(reader.readline())
  11     receive = asyncio.create_task(clients[me].get())
  12     while not reader.at_eof():
  13         done, pending = await asyncio.wait([send, receive], return_when=asyncio.FIRST_COMPLETED)
  14         for q in done:
  15             if q is send:
  16                 send = asyncio.create_task(reader.readline())
  17                 for out in clients.values():
  18                     if out is not clients[me]:
  19                         await out.put(f"{me} {q.result().decode().strip()}")
  20             elif q is receive:
  21                 receive = asyncio.create_task(clients[me].get())
  22                 writer.write(f"{q.result()}\n".encode())
  23                 await writer.drain()
  24     send.cancel()
  25     receive.cancel()
  26     print(me, "DONE")
  27     del clients[me]
  28     writer.close()
  29     await writer.wait_closed()
  30 
  31 async def main():
  32     server = await asyncio.start_server(chat, '0.0.0.0', 1337)
  33     async with server:
  34         await server.serve_forever()
  35 
  36 asyncio.run(main())

Д/З

  1. Почитать про asyncio

  2. Превратить «общий чат» в «коровий» следующим образом:
    • Вводимые строки состоят из команды с возможными параметрами
    • Вместо get_extra_info('peername') уникальным идентификатором пользователя является название коровы из python-cowsay, под которым он регистрируется

      • Пока пользователь не зарегистрировался, он не имеет право ни писать, ни получать сообщения
    • Сообщения оформляются с помощью cowsay() из модуля python-cowsay

    • Команды:
      • who — просмотр зарегистрированных пользователей

      • cows — просмотр свободных имён коров

      • login название_коровы — зарегистрироваться под именем название_коровы

      • say название_коровы текст сообщения — послать сообщение пользователю название_коровы

      • yield текст сообщения — послать сообщение всем зарегистрированным пользователям

      • quit — отключиться

  3. Вместо клиента можно пользоваться либо системным netcat, либо скриптом из простого модуля netcat

  4. Разработку вести согласно дисциплине оформления коммитов в подкаталоге 05_DiffPatchNet отчётного репозитория по Д/З

  5. Предполагается, что модуль python-cowsay устанавливается в окружение с помощью pipenv, в каталоге должен присутствовать соответствующий Pipfile

LecturesCMC/PythonDevelopment2024/05_DiffPatchNet (последним исправлял пользователь FrBrGeorge 2024-03-25 20:26:02)