Skip to content

Commit

Permalink
Merge pull request #258 from vkottler/dev/tftp-improvements
Browse files Browse the repository at this point in the history
Dev/tftp improvements
  • Loading branch information
vkottler authored Jul 22, 2024
2 parents aed589a + aad083c commit c65a820
Show file tree
Hide file tree
Showing 19 changed files with 179 additions and 57 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/python-package.yml
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ jobs:
- run: |
mk python-release owner=vkottler \
repo=runtimepy version=5.4.1
repo=runtimepy version=5.4.2
if: |
matrix.python-version == '3.12'
&& matrix.system == 'ubuntu-latest'
Expand Down
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@
=====================================
generator=datazen
version=3.1.4
hash=039855eb758d9eb1ea70df0654e31b61
hash=4f8a71a6066638ed1a90f375188f0578
=====================================
-->

# runtimepy ([5.4.1](https://pypi.org/project/runtimepy/))
# runtimepy ([5.4.2](https://pypi.org/project/runtimepy/))

[![python](https://img.shields.io/pypi/pyversions/runtimepy.svg)](https://pypi.org/project/runtimepy/)
![Build Status](https://github.com/vkottler/runtimepy/workflows/Python%20Package/badge.svg)
Expand Down Expand Up @@ -155,7 +155,7 @@ options:
$ ./venv3.12/bin/runtimepy tftp -h
usage: runtimepy tftp [-h] [-p PORT] [-m MODE] [-t TIMEOUT] [-r REEMIT]
{read,write} host our_file their_file
{read,write} host our_file [their_file]
positional arguments:
{read,write} action to perform
Expand Down
1 change: 1 addition & 0 deletions local/arbiter/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
test.txt
2 changes: 2 additions & 0 deletions local/configs/package.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@
name: runtimepy
version: "{{major}}.{{minor}}.{{patch}}"
description: A framework for implementing Python services.

entry: {{entry}}
time_command: true

requirements:
- vcorelib>=3.3.1
Expand Down
2 changes: 1 addition & 1 deletion local/variables/package.yaml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
major: 5
minor: 4
patch: 1
patch: 2
entry: runtimepy
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta:__legacy__"

[project]
name = "runtimepy"
version = "5.4.1"
version = "5.4.2"
description = "A framework for implementing Python services."
readme = "README.md"
requires-python = ">=3.11"
Expand Down
4 changes: 2 additions & 2 deletions runtimepy/__init__.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# =====================================
# generator=datazen
# version=3.1.4
# hash=acff0a8ea3e1379494862c3188de7a76
# hash=e6970089f5f2935c496cb3e9bb06b774
# =====================================

"""
Expand All @@ -10,7 +10,7 @@

DESCRIPTION = "A framework for implementing Python services."
PKG_NAME = "runtimepy"
VERSION = "5.4.1"
VERSION = "5.4.2"

# runtimepy-specific content.
METRICS_NAME = "metrics"
Expand Down
9 changes: 7 additions & 2 deletions runtimepy/commands/tftp.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import argparse
import asyncio
from pathlib import Path
from socket import getaddrinfo

from vcorelib.args import CommandFunction

Expand All @@ -21,7 +22,8 @@
def tftp_cmd(args: argparse.Namespace) -> int:
"""Execute the tftp command."""

addr = (args.host, args.port)
# Resolve hostname as early as possible.
addr = (getaddrinfo(args.host, None)[0][4][0], args.port)

stop_sig = asyncio.Event()
kwargs = {
Expand All @@ -31,6 +33,9 @@ def tftp_cmd(args: argparse.Namespace) -> int:
"process_kwargs": {"stop_sig": stop_sig},
}

if not args.their_file:
args.their_file = str(args.our_file)

if args.operation == "read":
task = tftp_read(addr, args.our_file, args.their_file, **kwargs)
else:
Expand Down Expand Up @@ -81,6 +86,6 @@ def add_tftp_cmd(parser: argparse.ArgumentParser) -> CommandFunction:
parser.add_argument("host", help="host to message")

parser.add_argument("our_file", type=Path, help="path to our file")
parser.add_argument("their_file", type=str, help="path to their file")
parser.add_argument("their_file", nargs="?", help="path to their file")

return tftp_cmd
2 changes: 1 addition & 1 deletion runtimepy/data/tftp_server.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,4 @@ clients:
- factory: tftp
name: tftp_server
kwargs:
local_addr: [localhost, "$tftp_server"]
local_addr: [127.0.0.1, "$tftp_server"]
8 changes: 5 additions & 3 deletions runtimepy/entry.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# =====================================
# generator=datazen
# version=3.1.4
# hash=c2bc26deadfa7cc275e815f499693863
# hash=79c31d1280a6e97b5d326aecb758c597
# =====================================

"""
Expand All @@ -10,13 +10,14 @@

# built-in
import argparse
from logging import getLogger
import os
from pathlib import Path
import sys
from typing import List

# third-party
from vcorelib.logging import init_logging, logging_args
from vcorelib.logging import init_logging, log_time, logging_args

# internal
from runtimepy import DESCRIPTION, VERSION
Expand Down Expand Up @@ -68,7 +69,8 @@ def main(argv: List[str] = None) -> int:
os.chdir(args.dir)

# run the application
result = entry(args)
with log_time(getLogger(__name__), "Command"):
result = entry(args)
except SystemExit as exc:
result = 1
if exc.code is not None and isinstance(exc.code, int):
Expand Down
13 changes: 8 additions & 5 deletions runtimepy/net/udp/connection.py
Original file line number Diff line number Diff line change
Expand Up @@ -126,14 +126,17 @@ def callback(transport_protocol: UdpTransportProtocol) -> None:
)
return result is not None

should_connect: bool = True

@classmethod
async def create_connection(
cls: type[T], connect: bool = True, **kwargs
) -> T:
async def create_connection(cls: type[T], **kwargs) -> T:
"""Create a UDP connection."""

LOG.debug("kwargs: %s", kwargs)

# Allows certain connections to have more sane defaults.
connect = kwargs.pop("connect", cls.should_connect)

# If the caller specifies a remote address but doesn't want a connected
# socket, handle this after initial creation.
remote_addr = None
Expand Down Expand Up @@ -173,8 +176,8 @@ async def create_pair(cls: type[T]) -> tuple[T, T]:
sock1.connect(("localhost", sock2.getsockname()[1]))
sock2.connect(("localhost", sock1.getsockname()[1]))

conn1 = await cls.create_connection(sock=sock1)
conn2 = await cls.create_connection(sock=sock2)
conn1 = await cls.create_connection(sock=sock1, connect=True)
conn2 = await cls.create_connection(sock=sock2, connect=True)
assert conn1.remote_address is not None
assert conn2.remote_address is not None

Expand Down
53 changes: 27 additions & 26 deletions runtimepy/net/udp/tftp/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,19 +38,19 @@ async def request_read(
) -> bool:
"""Request a tftp read operation."""

endpoint = self.endpoint(addr)
end_of_data = False
idx = 1

def ack_sender() -> None:
"""Send acks."""
nonlocal idx
self.send_ack(block=idx - 1, addr=addr)

async with AsyncExitStack() as stack:
# Claim read lock and ignore cancellation.
stack.enter_context(suppress(asyncio.CancelledError))
await stack.enter_async_context(endpoint.lock)

endpoint, event = await self._await_first_block(stack, addr=addr)

def ack_sender() -> None:
"""Send acks."""
nonlocal idx
endpoint.ack_sender(idx - 1, endpoint.addr)

def send_rrq() -> None:
"""Send request"""
Expand All @@ -60,9 +60,6 @@ def send_rrq() -> None:
"Requesting '%s' (%s) -> %s.", filename, mode, destination
)

event = asyncio.Event()
endpoint.awaiting_blocks[idx] = event

with self.log_time("Awaiting first data block", reminder=True):
# Wait for first data block.
if not await repeat_until(
Expand Down Expand Up @@ -112,21 +109,22 @@ def write_block() -> None:
if success:
write_block()

# Repeat last ack in the background.
if end_of_data:
self._conn_tasks.append(
asyncio.create_task(
repeat_until( # type: ignore
ack_sender,
asyncio.Event(),
endpoint.period.value,
endpoint.timeout.value,
# Repeat last ack in the background.
if end_of_data:
self._conn_tasks.append(
asyncio.create_task(
repeat_until( # type: ignore
ack_sender,
asyncio.Event(),
endpoint.period.value,
endpoint.timeout.value,
)
)
)
)

# Make a to-string or log method for vcorelib FileInfo?
#
# Ensure at least one ack sends.
await asyncio.sleep(0.01)

self.logger.info(
"Read %s (%s).",
FileInfo.from_file(destination),
Expand All @@ -146,16 +144,14 @@ async def request_write(
"""Request a tftp write operation."""

result = False
endpoint = self.endpoint(addr)

with as_path(source) as src:
async with AsyncExitStack() as stack:
# Claim write lock and ignore cancellation.
stack.enter_context(suppress(asyncio.CancelledError))
await stack.enter_async_context(endpoint.lock)

event = asyncio.Event()
endpoint.awaiting_acks[0] = event
# Set up first-ack handling.
endpoint, event = await self._await_first_ack(stack, addr=addr)

def send_wrq() -> None:
"""Send request."""
Expand Down Expand Up @@ -183,6 +179,11 @@ def send_wrq() -> None:
)

# Compare hashes.
self.logger.info(
"Reading '%s' %s.",
filename,
"succeeded" if result else "failed",
)
if result:
result = file_md5_hex(src) == file_md5_hex(tmp)
self.logger.info(
Expand Down
Loading

0 comments on commit c65a820

Please sign in to comment.