Compare commits

...

14 Commits

Author SHA1 Message Date
Pavol Rusnak
3b0c5cdfd3
Merge 06b9555462 into 30248854ed 2024-10-11 21:14:44 +00:00
Pavol Rusnak
06b9555462
remove dangling spaces 2024-10-11 23:14:32 +02:00
Pavol Rusnak
ce9dc56048
modernize 2024-10-11 23:13:52 +02:00
Mark Qvist
30248854ed Updated changelog 2024-10-11 17:13:03 +02:00
Mark Qvist
f34bc75588 Updated docs 2024-10-11 16:47:53 +02:00
Mark Qvist
3b23e2f37d Improved RNode BLE reconnection reliability 2024-10-11 13:38:16 +02:00
Mark Qvist
7417cf5947 Add rnode battery state to rnstatus output 2024-10-11 10:14:10 +02:00
Mark Qvist
60d8da843c Disable tty module dependency for rnx, since it is currently unused 2024-10-11 09:54:09 +02:00
Mark Qvist
f9667fd684 Fixed missing import on Android 2024-10-10 23:49:20 +02:00
Mark Qvist
d9269c6047 Updated version 2024-10-10 23:32:09 +02:00
Mark Qvist
6521f839cd Fixed resource transfers hanging for a long time over slow links if proof packet is lost 2024-10-10 17:06:43 +02:00
Mark Qvist
d63bbcdc0a Updated changelog 2024-10-10 00:45:09 +02:00
Mark Qvist
c36c7186de Updated docs 2024-10-10 00:44:33 +02:00
Mark Qvist
6fec76205c Added save directory option to rncp 2024-10-10 00:41:57 +02:00
104 changed files with 2373 additions and 2316 deletions

View File

@ -1,3 +1,36 @@
### 2024-10-11: RNS β 0.8.4
This release fixes a number of bugs and improves reliability of automatic reconnection when BLE-connected RNodes unexpectedly disappear or lose connection.
**Changes**
- Improved RNode BLE reconnection realiability
- Added RNode battery state to `rnstatus` output
- Fixed resource transfer hanging for a long time over slow links if proof packet is lost
- Fixed missing import on Android
**Release Hashes**
```
d3f7a9fddc6c1e59b1e4895756fe602408ac6ef09de377ee65ec62d09fff97a3 dist/rns-0.8.4-py3-none-any.whl
eb3843bcab1428be0adb097988991229a4c03156ab40cc9c6e2d9c590d8b850b dist/rnspure-0.8.4-py3-none-any.whl
```
### 2024-10-10: RNS β 0.8.3
This release fixes a bug in resource transfer progress calculation, improves RNode error handling, and brings minor improvements to the `rncp` utility.
**Changes**
- Fixed a bug in resource transfer progress calculations
- Added physical layer transfer rate output option to `rncp`
- Added save directory option to `rncp`
- Improved path handling for the fetch-jail option of of `rncp`
- Added error detection for modem communication timeouts on connected RNode devices
**Release Hashes**
```
54ddab32769081045db5fe45b27492cc012bf2fad64bc65ed37011f3651469fb rns-0.8.3-py3-none-any.whl
a04915111d65b05a5f2ef2687ed208813034196c0c5e711cb01e6db72faa23ef rnspure-0.8.3-py3-none-any.whl
```
### 2024-10-06: RNS β 0.8.2 ### 2024-10-06: RNS β 0.8.2
This release adds several new boards to `rnodeconf`, fixes a range of bugs and improves transport reliability. This release adds several new boards to `rnodeconf`, fixes a range of bugs and improves transport reliability.

View File

@ -93,9 +93,7 @@ def announceLoop(destination_1, destination_2):
# Send the announce including the app data # Send the announce including the app data
destination_1.announce(app_data=fruit.encode("utf-8")) destination_1.announce(app_data=fruit.encode("utf-8"))
RNS.log( RNS.log(
"Sent announce from "+ f"Sent announce from {RNS.prettyhexrep(destination_1.hash)} ({destination_1.name})"
RNS.prettyhexrep(destination_1.hash)+
" ("+destination_1.name+")"
) )
# Randomly select a noble gas # Randomly select a noble gas
@ -104,9 +102,7 @@ def announceLoop(destination_1, destination_2):
# Send the announce including the app data # Send the announce including the app data
destination_2.announce(app_data=noble_gas.encode("utf-8")) destination_2.announce(app_data=noble_gas.encode("utf-8"))
RNS.log( RNS.log(
"Sent announce from "+ f"Sent announce from {RNS.prettyhexrep(destination_2.hash)} ({destination_2.name})"
RNS.prettyhexrep(destination_2.hash)+
" ("+destination_2.name+")"
) )
# We will need to define an announce handler class that # We will need to define an announce handler class that
@ -126,14 +122,12 @@ class ExampleAnnounceHandler:
# and cannot use wildcards. # and cannot use wildcards.
def received_announce(self, destination_hash, announced_identity, app_data): def received_announce(self, destination_hash, announced_identity, app_data):
RNS.log( RNS.log(
"Received an announce from "+ f"Received an announce from {RNS.prettyhexrep(destination_hash)}"
RNS.prettyhexrep(destination_hash)
) )
if app_data: if app_data:
RNS.log( RNS.log(
"The announce contained the following app data: "+ f"The announce contained the following app data: {app_data.decode('utf-8')}"
app_data.decode("utf-8")
) )
########################################################## ##########################################################

View File

@ -48,15 +48,13 @@ def program_setup(configpath, channel=None):
def packet_callback(data, packet): def packet_callback(data, packet):
# Simply print out the received data # Simply print out the received data
print("") print("")
print("Received data: "+data.decode("utf-8")+"\r\n> ", end="") print(f"Received data: {data.decode('utf-8')}\r\n> ", end="")
sys.stdout.flush() sys.stdout.flush()
def broadcastLoop(destination): def broadcastLoop(destination):
# Let the user know that everything is ready # Let the user know that everything is ready
RNS.log( RNS.log(
"Broadcast example "+ f"Broadcast example {RNS.prettyhexrep(destination.hash)} running, enter text and hit enter to broadcast (Ctrl-C to quit)"
RNS.prettyhexrep(destination.hash)+
" running, enter text and hit enter to broadcast (Ctrl-C to quit)"
) )
# We enter a loop that runs until the users exits. # We enter a loop that runs until the users exits.

View File

@ -61,9 +61,7 @@ def server(configpath):
def server_loop(destination): def server_loop(destination):
# Let the user know that everything is ready # Let the user know that everything is ready
RNS.log( RNS.log(
"Link buffer example "+ f"Link buffer example {RNS.prettyhexrep(destination.hash)} running, waiting for a connection."
RNS.prettyhexrep(destination.hash)+
" running, waiting for a connection."
) )
RNS.log("Hit enter to manually send an announce (Ctrl-C to quit)") RNS.log("Hit enter to manually send an announce (Ctrl-C to quit)")
@ -75,7 +73,7 @@ def server_loop(destination):
while True: while True:
entered = input() entered = input()
destination.announce() destination.announce()
RNS.log("Sent announce from "+RNS.prettyhexrep(destination.hash)) RNS.log(f"Sent announce from {RNS.prettyhexrep(destination.hash)}")
# When a client establishes a link to our server # When a client establishes a link to our server
# destination, this function will be called with # destination, this function will be called with
@ -120,9 +118,9 @@ def server_buffer_ready(ready_bytes: int):
data = latest_buffer.read(ready_bytes) data = latest_buffer.read(ready_bytes)
data = data.decode("utf-8") data = data.decode("utf-8")
RNS.log("Received data over the buffer: " + data) RNS.log(f"Received data over the buffer: {data}")
reply_message = "I received \""+data+"\" over the buffer" reply_message = f"I received \"{data}\" over the buffer"
reply_message = reply_message.encode("utf-8") reply_message = reply_message.encode("utf-8")
latest_buffer.write(reply_message) latest_buffer.write(reply_message)
latest_buffer.flush() latest_buffer.flush()
@ -151,7 +149,7 @@ def client(destination_hexhash, configpath):
dest_len = (RNS.Reticulum.TRUNCATED_HASHLENGTH//8)*2 dest_len = (RNS.Reticulum.TRUNCATED_HASHLENGTH//8)*2
if len(destination_hexhash) != dest_len: if len(destination_hexhash) != dest_len:
raise ValueError( raise ValueError(
"Destination length is invalid, must be {hex} hexadecimal characters ({byte} bytes).".format(hex=dest_len, byte=dest_len//2) f"Destination length is invalid, must be {dest_len} hexadecimal characters ({dest_len // 2} bytes)."
) )
destination_hash = bytes.fromhex(destination_hexhash) destination_hash = bytes.fromhex(destination_hexhash)
@ -223,7 +221,7 @@ def client_loop():
except Exception as e: except Exception as e:
RNS.log("Error while sending data over the link buffer: "+str(e)) RNS.log(f"Error while sending data over the link buffer: {e}")
should_quit = True should_quit = True
server_link.teardown() server_link.teardown()
@ -262,7 +260,7 @@ def link_closed(link):
def client_buffer_ready(ready_bytes: int): def client_buffer_ready(ready_bytes: int):
global buffer global buffer
data = buffer.read(ready_bytes) data = buffer.read(ready_bytes)
RNS.log("Received data over the link buffer: " + data.decode("utf-8")) RNS.log(f"Received data over the link buffer: {data.decode('utf-8')}")
print("> ", end=" ") print("> ", end=" ")
sys.stdout.flush() sys.stdout.flush()

View File

@ -124,9 +124,7 @@ def server(configpath):
def server_loop(destination): def server_loop(destination):
# Let the user know that everything is ready # Let the user know that everything is ready
RNS.log( RNS.log(
"Link example "+ f"Link example {RNS.prettyhexrep(destination.hash)} running, waiting for a connection."
RNS.prettyhexrep(destination.hash)+
" running, waiting for a connection."
) )
RNS.log("Hit enter to manually send an announce (Ctrl-C to quit)") RNS.log("Hit enter to manually send an announce (Ctrl-C to quit)")
@ -138,7 +136,7 @@ def server_loop(destination):
while True: while True:
entered = input() entered = input()
destination.announce() destination.announce()
RNS.log("Sent announce from "+RNS.prettyhexrep(destination.hash)) RNS.log(f"Sent announce from {RNS.prettyhexrep(destination.hash)}")
# When a client establishes a link to our server # When a client establishes a link to our server
# destination, this function will be called with # destination, this function will be called with
@ -176,9 +174,9 @@ def server_message_received(message):
# #
# #
if isinstance(message, StringMessage): if isinstance(message, StringMessage):
RNS.log("Received data on the link: " + message.data + " (message created at " + str(message.timestamp) + ")") RNS.log(f"Received data on the link: {message.data} (message created at {message.timestamp})")
reply_message = StringMessage("I received \""+message.data+"\" over the link") reply_message = StringMessage(f"I received \"{message.data}\" over the link")
latest_client_link.get_channel().send(reply_message) latest_client_link.get_channel().send(reply_message)
# Incoming messages are sent to each message # Incoming messages are sent to each message
@ -206,7 +204,7 @@ def client(destination_hexhash, configpath):
dest_len = (RNS.Reticulum.TRUNCATED_HASHLENGTH//8)*2 dest_len = (RNS.Reticulum.TRUNCATED_HASHLENGTH//8)*2
if len(destination_hexhash) != dest_len: if len(destination_hexhash) != dest_len:
raise ValueError( raise ValueError(
"Destination length is invalid, must be {hex} hexadecimal characters ({byte} bytes).".format(hex=dest_len, byte=dest_len//2) f"Destination length is invalid, must be {dest_len} hexadecimal characters ({dest_len // 2} bytes)."
) )
destination_hash = bytes.fromhex(destination_hexhash) destination_hash = bytes.fromhex(destination_hexhash)
@ -280,17 +278,14 @@ def client_loop():
channel.send(message) channel.send(message)
else: else:
RNS.log( RNS.log(
"Cannot send this packet, the data size of "+ f"Cannot send this packet, the data size of {packed_size} bytes exceeds the link packet MDU of {channel.MDU} bytes",
str(packed_size)+" bytes exceeds the link packet MDU of "+
str(channel.MDU)+" bytes",
RNS.LOG_ERROR RNS.LOG_ERROR
) )
else: else:
RNS.log("Channel is not ready to send, please wait for " + RNS.log(f"Channel is not ready to send, please wait for pending messages to complete.", RNS.LOG_ERROR)
"pending messages to complete.", RNS.LOG_ERROR)
except Exception as e: except Exception as e:
RNS.log("Error while sending data over the link: "+str(e)) RNS.log(f"Error while sending data over the link: {e}")
should_quit = True should_quit = True
server_link.teardown() server_link.teardown()
@ -329,7 +324,7 @@ def link_closed(link):
# simply print out the data. # simply print out the data.
def client_message_received(message): def client_message_received(message):
if isinstance(message, StringMessage): if isinstance(message, StringMessage):
RNS.log("Received data on the link: " + message.data + " (message created at " + str(message.timestamp) + ")") RNS.log(f"Received data on the link: {message.data} (message created at {message.timestamp})")
print("> ", end=" ") print("> ", end=" ")
sys.stdout.flush() sys.stdout.flush()

View File

@ -64,9 +64,7 @@ def server(configpath):
def announceLoop(destination): def announceLoop(destination):
# Let the user know that everything is ready # Let the user know that everything is ready
RNS.log( RNS.log(
"Echo server "+ f"Echo server {RNS.prettyhexrep(destination.hash)} running, hit enter to manually send an announce (Ctrl-C to quit)"
RNS.prettyhexrep(destination.hash)+
" running, hit enter to manually send an announce (Ctrl-C to quit)"
) )
# We enter a loop that runs until the users exits. # We enter a loop that runs until the users exits.
@ -76,7 +74,7 @@ def announceLoop(destination):
while True: while True:
entered = input() entered = input()
destination.announce() destination.announce()
RNS.log("Sent announce from "+RNS.prettyhexrep(destination.hash)) RNS.log(f"Sent announce from {RNS.prettyhexrep(destination.hash)}")
def server_callback(message, packet): def server_callback(message, packet):
@ -93,19 +91,19 @@ def server_callback(message, packet):
reception_snr = reticulum.get_packet_snr(packet.packet_hash) reception_snr = reticulum.get_packet_snr(packet.packet_hash)
if reception_rssi != None: if reception_rssi != None:
reception_stats += " [RSSI "+str(reception_rssi)+" dBm]" reception_stats += f" [RSSI {reception_rssi} dBm]"
if reception_snr != None: if reception_snr != None:
reception_stats += " [SNR "+str(reception_snr)+" dBm]" reception_stats += f" [SNR {reception_snr} dBm]"
else: else:
if packet.rssi != None: if packet.rssi != None:
reception_stats += " [RSSI "+str(packet.rssi)+" dBm]" reception_stats += f" [RSSI {packet.rssi} dBm]"
if packet.snr != None: if packet.snr != None:
reception_stats += " [SNR "+str(packet.snr)+" dB]" reception_stats += f" [SNR {packet.snr} dB]"
RNS.log("Received packet from echo client, proof sent"+reception_stats) RNS.log(f"Received packet from echo client, proof sent{reception_stats}")
########################################################## ##########################################################
@ -123,13 +121,13 @@ def client(destination_hexhash, configpath, timeout=None):
dest_len = (RNS.Reticulum.TRUNCATED_HASHLENGTH//8)*2 dest_len = (RNS.Reticulum.TRUNCATED_HASHLENGTH//8)*2
if len(destination_hexhash) != dest_len: if len(destination_hexhash) != dest_len:
raise ValueError( raise ValueError(
"Destination length is invalid, must be {hex} hexadecimal characters ({byte} bytes).".format(hex=dest_len, byte=dest_len//2) f"Destination length is invalid, must be {dest_len} hexadecimal characters ({dest_len // 2} bytes)."
) )
destination_hash = bytes.fromhex(destination_hexhash) destination_hash = bytes.fromhex(destination_hexhash)
except Exception as e: except Exception as e:
RNS.log("Invalid destination entered. Check your input!") RNS.log("Invalid destination entered. Check your input!")
RNS.log(str(e)+"\n") RNS.log(f"{e}\n")
exit() exit()
# We must first initialise Reticulum # We must first initialise Reticulum
@ -142,9 +140,7 @@ def client(destination_hexhash, configpath, timeout=None):
# Tell the user that the client is ready! # Tell the user that the client is ready!
RNS.log( RNS.log(
"Echo client ready, hit enter to send echo request to "+ f"Echo client ready, hit enter to send echo request to {destination_hexhash} (Ctrl-C to quit)"
destination_hexhash+
" (Ctrl-C to quit)"
) )
# We enter a loop that runs until the user exits. # We enter a loop that runs until the user exits.
@ -205,7 +201,7 @@ def client(destination_hexhash, configpath, timeout=None):
packet_receipt.set_delivery_callback(packet_delivered) packet_receipt.set_delivery_callback(packet_delivered)
# Tell the user that the echo request was sent # Tell the user that the echo request was sent
RNS.log("Sent echo request to "+RNS.prettyhexrep(request_destination.hash)) RNS.log(f"Sent echo request to {RNS.prettyhexrep(request_destination.hash)}")
else: else:
# If we do not know this destination, tell the # If we do not know this destination, tell the
# user to wait for an announce to arrive. # user to wait for an announce to arrive.
@ -222,10 +218,10 @@ def packet_delivered(receipt):
rtt = receipt.get_rtt() rtt = receipt.get_rtt()
if (rtt >= 1): if (rtt >= 1):
rtt = round(rtt, 3) rtt = round(rtt, 3)
rttstring = str(rtt)+" seconds" rttstring = f"{rtt} seconds"
else: else:
rtt = round(rtt*1000, 3) rtt = round(rtt*1000, 3)
rttstring = str(rtt)+" milliseconds" rttstring = f"{rtt} milliseconds"
reception_stats = "" reception_stats = ""
if reticulum.is_connected_to_shared_instance: if reticulum.is_connected_to_shared_instance:
@ -233,30 +229,27 @@ def packet_delivered(receipt):
reception_snr = reticulum.get_packet_snr(receipt.proof_packet.packet_hash) reception_snr = reticulum.get_packet_snr(receipt.proof_packet.packet_hash)
if reception_rssi != None: if reception_rssi != None:
reception_stats += " [RSSI "+str(reception_rssi)+" dBm]" reception_stats += f" [RSSI {reception_rssi} dBm]"
if reception_snr != None: if reception_snr != None:
reception_stats += " [SNR "+str(reception_snr)+" dB]" reception_stats += f" [SNR {reception_snr} dB]"
else: else:
if receipt.proof_packet != None: if receipt.proof_packet != None:
if receipt.proof_packet.rssi != None: if receipt.proof_packet.rssi != None:
reception_stats += " [RSSI "+str(receipt.proof_packet.rssi)+" dBm]" reception_stats += f" [RSSI {receipt.proof_packet.rssi} dBm]"
if receipt.proof_packet.snr != None: if receipt.proof_packet.snr != None:
reception_stats += " [SNR "+str(receipt.proof_packet.snr)+" dB]" reception_stats += f" [SNR {receipt.proof_packet.snr} dB]"
RNS.log( RNS.log(
"Valid reply received from "+ f"Valid reply received from {RNS.prettyhexrep(receipt.destination.hash)}, round-trip time is {rttstring}{reception_stats}"
RNS.prettyhexrep(receipt.destination.hash)+
", round-trip time is "+rttstring+
reception_stats
) )
# This function is called if a packet times out. # This function is called if a packet times out.
def packet_timed_out(receipt): def packet_timed_out(receipt):
if receipt.status == RNS.PacketReceipt.FAILED: if receipt.status == RNS.PacketReceipt.FAILED:
RNS.log("Packet "+RNS.prettyhexrep(receipt.hash)+" timed out") RNS.log(f"Packet {RNS.prettyhexrep(receipt.hash)} timed out")
########################################################## ##########################################################

View File

@ -73,7 +73,7 @@ def server(configpath, path):
def announceLoop(destination): def announceLoop(destination):
# Let the user know that everything is ready # Let the user know that everything is ready
RNS.log("File server "+RNS.prettyhexrep(destination.hash)+" running") RNS.log(f"File server {RNS.prettyhexrep(destination.hash)} running")
RNS.log("Hit enter to manually send an announce (Ctrl-C to quit)") RNS.log("Hit enter to manually send an announce (Ctrl-C to quit)")
# We enter a loop that runs until the users exits. # We enter a loop that runs until the users exits.
@ -83,7 +83,7 @@ def announceLoop(destination):
while True: while True:
entered = input() entered = input()
destination.announce() destination.announce()
RNS.log("Sent announce from "+RNS.prettyhexrep(destination.hash)) RNS.log(f"Sent announce from {RNS.prettyhexrep(destination.hash)}")
# Here's a convenience function for listing all files # Here's a convenience function for listing all files
# in our served directory # in our served directory
@ -145,7 +145,7 @@ def client_request(message, packet):
try: try:
# If we have the requested file, we'll # If we have the requested file, we'll
# read it and pack it as a resource # read it and pack it as a resource
RNS.log("Client requested \""+filename+"\"") RNS.log(f"Client requested \"{filename}\"")
file = open(os.path.join(serve_path, filename), "rb") file = open(os.path.join(serve_path, filename), "rb")
file_resource = RNS.Resource( file_resource = RNS.Resource(
@ -158,7 +158,7 @@ def client_request(message, packet):
except Exception as e: except Exception as e:
# If somethign went wrong, we close # If somethign went wrong, we close
# the link # the link
RNS.log("Error while reading file \""+filename+"\"", RNS.LOG_ERROR) RNS.log(f"Error while reading file \"{filename}\"", RNS.LOG_ERROR)
packet.link.teardown() packet.link.teardown()
raise e raise e
else: else:
@ -175,9 +175,9 @@ def resource_sending_concluded(resource):
name = "resource" name = "resource"
if resource.status == RNS.Resource.COMPLETE: if resource.status == RNS.Resource.COMPLETE:
RNS.log("Done sending \""+name+"\" to client") RNS.log(f"Done sending \"{name}\" to client")
elif resource.status == RNS.Resource.FAILED: elif resource.status == RNS.Resource.FAILED:
RNS.log("Sending \""+name+"\" to client failed") RNS.log(f"Sending \"{name}\" to client failed")
def list_delivered(receipt): def list_delivered(receipt):
RNS.log("The file list was received by the client") RNS.log("The file list was received by the client")
@ -218,7 +218,7 @@ def client(destination_hexhash, configpath):
dest_len = (RNS.Reticulum.TRUNCATED_HASHLENGTH//8)*2 dest_len = (RNS.Reticulum.TRUNCATED_HASHLENGTH//8)*2
if len(destination_hexhash) != dest_len: if len(destination_hexhash) != dest_len:
raise ValueError( raise ValueError(
"Destination length is invalid, must be {hex} hexadecimal characters ({byte} bytes).".format(hex=dest_len, byte=dest_len//2) f"Destination length is invalid, must be {dest_len} hexadecimal characters ({dest_len // 2} bytes)."
) )
destination_hash = bytes.fromhex(destination_hexhash) destination_hash = bytes.fromhex(destination_hexhash)
@ -293,7 +293,7 @@ def download(filename):
request_packet.send() request_packet.send()
print("") print("")
print(("Requested \""+filename+"\" from server, waiting for download to begin...")) print(f"Requested \"{filename}\" from server, waiting for download to begin...")
menu_mode = "download_started" menu_mode = "download_started"
# This function runs a simple menu for the user # This function runs a simple menu for the user
@ -363,7 +363,7 @@ def print_menu():
while menu_mode == "downloading": while menu_mode == "downloading":
global current_download global current_download
percent = round(current_download.get_progress() * 100.0, 1) percent = round(current_download.get_progress() * 100.0, 1)
print(("\rProgress: "+str(percent)+" % "), end=' ') print(f'\rProgress: {percent} % ', end=' ')
sys.stdout.flush() sys.stdout.flush()
time.sleep(0.1) time.sleep(0.1)
@ -383,15 +383,15 @@ def print_menu():
# Print statistics # Print statistics
hours, rem = divmod(download_time, 3600) hours, rem = divmod(download_time, 3600)
minutes, seconds = divmod(rem, 60) minutes, seconds = divmod(rem, 60)
timestring = "{:0>2}:{:0>2}:{:05.2f}".format(int(hours),int(minutes),seconds) timestring = f"{int(hours):0>2}:{int(minutes):0>2}:{seconds:05.2f}"
print("") print("")
print("") print("")
print("--- Statistics -----") print("--- Statistics -----")
print("\tTime taken : "+timestring) print(f"\tTime taken : {timestring}")
print("\tFile size : "+size_str(file_size)) print(f"\tFile size : {size_str(file_size)}")
print("\tData transferred : "+size_str(transfer_size)) print(f"\tData transferred : {size_str(transfer_size)}")
print("\tEffective rate : "+size_str(file_size/download_time, suffix='b')+"/s") print(f"\tEffective rate : {size_str(file_size / download_time, suffix='b')}/s")
print("\tTransfer rate : "+size_str(transfer_size/download_time, suffix='b')+"/s") print(f"\tTransfer rate : {size_str(transfer_size / download_time, suffix='b')}/s")
print("") print("")
print("The download completed! Press enter to return to the menu.") print("The download completed! Press enter to return to the menu.")
print("") print("")
@ -412,7 +412,7 @@ def print_filelist():
global server_files global server_files
print("Files on server:") print("Files on server:")
for index,file in enumerate(server_files): for index,file in enumerate(server_files):
print("\t("+str(index)+")\t"+file) print(f"\t({index})\t{file}")
def filelist_received(filelist_data, packet): def filelist_received(filelist_data, packet):
global server_files, menu_mode global server_files, menu_mode
@ -509,7 +509,7 @@ def download_concluded(resource):
counter = 0 counter = 0
while os.path.isfile(saved_filename): while os.path.isfile(saved_filename):
counter += 1 counter += 1
saved_filename = current_filename+"."+str(counter) saved_filename = f"{current_filename}.{counter}"
try: try:
file = open(saved_filename, "wb") file = open(saved_filename, "wb")
@ -534,9 +534,9 @@ def size_str(num, suffix='B'):
for unit in units: for unit in units:
if abs(num) < 1024.0: if abs(num) < 1024.0:
return "%3.2f %s%s" % (num, unit, suffix) return f"{num:3.2f} {unit}{suffix}"
num /= 1024.0 num /= 1024.0
return "%.2f %s%s" % (num, last_unit, suffix) return f"{num:.2f} {last_unit}{suffix}"
# A convenience function for clearing the screen # A convenience function for clearing the screen
def clear_screen(): def clear_screen():

View File

@ -53,9 +53,7 @@ def server(configpath):
def server_loop(destination): def server_loop(destination):
# Let the user know that everything is ready # Let the user know that everything is ready
RNS.log( RNS.log(
"Link identification example "+ f"Link identification example {RNS.prettyhexrep(destination.hash)} running, waiting for a connection."
RNS.prettyhexrep(destination.hash)+
" running, waiting for a connection."
) )
RNS.log("Hit enter to manually send an announce (Ctrl-C to quit)") RNS.log("Hit enter to manually send an announce (Ctrl-C to quit)")
@ -67,7 +65,7 @@ def server_loop(destination):
while True: while True:
entered = input() entered = input()
destination.announce() destination.announce()
RNS.log("Sent announce from "+RNS.prettyhexrep(destination.hash)) RNS.log(f"Sent announce from {RNS.prettyhexrep(destination.hash)}")
# When a client establishes a link to our server # When a client establishes a link to our server
# destination, this function will be called with # destination, this function will be called with
@ -85,7 +83,7 @@ def client_disconnected(link):
RNS.log("Client disconnected") RNS.log("Client disconnected")
def remote_identified(link, identity): def remote_identified(link, identity):
RNS.log("Remote identified as: "+str(identity)) RNS.log(f"Remote identified as: {identity}")
def server_packet_received(message, packet): def server_packet_received(message, packet):
global latest_client_link global latest_client_link
@ -100,9 +98,9 @@ def server_packet_received(message, packet):
# that connected. # that connected.
text = message.decode("utf-8") text = message.decode("utf-8")
RNS.log("Received data from "+remote_peer+": "+text) RNS.log(f"Received data from {remote_peer}: {text}")
reply_text = "I received \""+text+"\" over the link from "+remote_peer reply_text = f"I received \"{text}\" over the link from {remote_peer}"
reply_data = reply_text.encode("utf-8") reply_data = reply_text.encode("utf-8")
RNS.Packet(latest_client_link, reply_data).send() RNS.Packet(latest_client_link, reply_data).send()
@ -127,7 +125,7 @@ def client(destination_hexhash, configpath):
dest_len = (RNS.Reticulum.TRUNCATED_HASHLENGTH//8)*2 dest_len = (RNS.Reticulum.TRUNCATED_HASHLENGTH//8)*2
if len(destination_hexhash) != dest_len: if len(destination_hexhash) != dest_len:
raise ValueError( raise ValueError(
"Destination length is invalid, must be {hex} hexadecimal characters ({byte} bytes).".format(hex=dest_len, byte=dest_len//2) f"Destination length is invalid, must be {dest_len} hexadecimal characters ({dest_len // 2} bytes)."
) )
destination_hash = bytes.fromhex(destination_hexhash) destination_hash = bytes.fromhex(destination_hexhash)
@ -141,8 +139,7 @@ def client(destination_hexhash, configpath):
# Create a new client identity # Create a new client identity
client_identity = RNS.Identity() client_identity = RNS.Identity()
RNS.log( RNS.log(
"Client created new identity "+ f"Client created new identity {client_identity}"
str(client_identity)
) )
# Check if we know a path to the destination # Check if we know a path to the destination
@ -210,14 +207,12 @@ def client_loop():
RNS.Packet(server_link, data).send() RNS.Packet(server_link, data).send()
else: else:
RNS.log( RNS.log(
"Cannot send this packet, the data size of "+ f"Cannot send this packet, the data size of {len(data)} bytes exceeds the link packet MDU of {RNS.Link.MDU} bytes",
str(len(data))+" bytes exceeds the link packet MDU of "+
str(RNS.Link.MDU)+" bytes",
RNS.LOG_ERROR RNS.LOG_ERROR
) )
except Exception as e: except Exception as e:
RNS.log("Error while sending data over the link: "+str(e)) RNS.log(f"Error while sending data over the link: {e}")
should_quit = True should_quit = True
server_link.teardown() server_link.teardown()
@ -253,7 +248,7 @@ def link_closed(link):
# simply print out the data. # simply print out the data.
def client_packet_received(message, packet): def client_packet_received(message, packet):
text = message.decode("utf-8") text = message.decode("utf-8")
RNS.log("Received data on the link: "+text) RNS.log(f"Received data on the link: {text}")
print("> ", end=" ") print("> ", end=" ")
sys.stdout.flush() sys.stdout.flush()

View File

@ -53,9 +53,7 @@ def server(configpath):
def server_loop(destination): def server_loop(destination):
# Let the user know that everything is ready # Let the user know that everything is ready
RNS.log( RNS.log(
"Link example "+ f"Link example {RNS.prettyhexrep(destination.hash)} running, waiting for a connection."
RNS.prettyhexrep(destination.hash)+
" running, waiting for a connection."
) )
RNS.log("Hit enter to manually send an announce (Ctrl-C to quit)") RNS.log("Hit enter to manually send an announce (Ctrl-C to quit)")
@ -67,7 +65,7 @@ def server_loop(destination):
while True: while True:
entered = input() entered = input()
destination.announce() destination.announce()
RNS.log("Sent announce from "+RNS.prettyhexrep(destination.hash)) RNS.log(f"Sent announce from {RNS.prettyhexrep(destination.hash)}")
# When a client establishes a link to our server # When a client establishes a link to our server
# destination, this function will be called with # destination, this function will be called with
@ -90,9 +88,9 @@ def server_packet_received(message, packet):
# it will all be directed to the last client # it will all be directed to the last client
# that connected. # that connected.
text = message.decode("utf-8") text = message.decode("utf-8")
RNS.log("Received data on the link: "+text) RNS.log(f"Received data on the link: {text}")
reply_text = "I received \""+text+"\" over the link" reply_text = f"I received \"{text}\" over the link"
reply_data = reply_text.encode("utf-8") reply_data = reply_text.encode("utf-8")
RNS.Packet(latest_client_link, reply_data).send() RNS.Packet(latest_client_link, reply_data).send()
@ -113,7 +111,7 @@ def client(destination_hexhash, configpath):
dest_len = (RNS.Reticulum.TRUNCATED_HASHLENGTH//8)*2 dest_len = (RNS.Reticulum.TRUNCATED_HASHLENGTH//8)*2
if len(destination_hexhash) != dest_len: if len(destination_hexhash) != dest_len:
raise ValueError( raise ValueError(
"Destination length is invalid, must be {hex} hexadecimal characters ({byte} bytes).".format(hex=dest_len, byte=dest_len//2) f"Destination length is invalid, must be {dest_len} hexadecimal characters ({dest_len // 2} bytes)."
) )
destination_hash = bytes.fromhex(destination_hexhash) destination_hash = bytes.fromhex(destination_hexhash)
@ -189,14 +187,12 @@ def client_loop():
RNS.Packet(server_link, data).send() RNS.Packet(server_link, data).send()
else: else:
RNS.log( RNS.log(
"Cannot send this packet, the data size of "+ f"Cannot send this packet, the data size of {len(data)} bytes exceeds the link packet MDU of {RNS.Link.MDU} bytes",
str(len(data))+" bytes exceeds the link packet MDU of "+
str(RNS.Link.MDU)+" bytes",
RNS.LOG_ERROR RNS.LOG_ERROR
) )
except Exception as e: except Exception as e:
RNS.log("Error while sending data over the link: "+str(e)) RNS.log(f"Error while sending data over the link: {e}")
should_quit = True should_quit = True
server_link.teardown() server_link.teardown()
@ -230,7 +226,7 @@ def link_closed(link):
# simply print out the data. # simply print out the data.
def client_packet_received(message, packet): def client_packet_received(message, packet):
text = message.decode("utf-8") text = message.decode("utf-8")
RNS.log("Received data on the link: "+text) RNS.log(f"Received data on the link: {text}")
print("> ", end=" ") print("> ", end=" ")
sys.stdout.flush() sys.stdout.flush()

View File

@ -51,9 +51,7 @@ def program_setup(configpath):
def announceLoop(destination): def announceLoop(destination):
# Let the user know that everything is ready # Let the user know that everything is ready
RNS.log( RNS.log(
"Minimal example "+ f"Minimal example {RNS.prettyhexrep(destination.hash)} running, hit enter to manually send an announce (Ctrl-C to quit)"
RNS.prettyhexrep(destination.hash)+
" running, hit enter to manually send an announce (Ctrl-C to quit)"
) )
# We enter a loop that runs until the users exits. # We enter a loop that runs until the users exits.
@ -63,7 +61,7 @@ def announceLoop(destination):
while True: while True:
entered = input() entered = input()
destination.announce() destination.announce()
RNS.log("Sent announce from "+RNS.prettyhexrep(destination.hash)) RNS.log(f"Sent announce from {RNS.prettyhexrep(destination.hash)}")
########################################################## ##########################################################

View File

@ -75,9 +75,7 @@ def server(configpath):
def announceLoop(destination): def announceLoop(destination):
# Let the user know that everything is ready # Let the user know that everything is ready
RNS.log( RNS.log(
"Ratcheted echo server "+ f"Ratcheted echo server {RNS.prettyhexrep(destination.hash)} running, hit enter to manually send an announce (Ctrl-C to quit)"
RNS.prettyhexrep(destination.hash)+
" running, hit enter to manually send an announce (Ctrl-C to quit)"
) )
# We enter a loop that runs until the users exits. # We enter a loop that runs until the users exits.
@ -87,7 +85,7 @@ def announceLoop(destination):
while True: while True:
entered = input() entered = input()
destination.announce() destination.announce()
RNS.log("Sent announce from "+RNS.prettyhexrep(destination.hash)) RNS.log(f"Sent announce from {RNS.prettyhexrep(destination.hash)}")
def server_callback(message, packet): def server_callback(message, packet):
@ -104,19 +102,19 @@ def server_callback(message, packet):
reception_snr = reticulum.get_packet_snr(packet.packet_hash) reception_snr = reticulum.get_packet_snr(packet.packet_hash)
if reception_rssi != None: if reception_rssi != None:
reception_stats += " [RSSI "+str(reception_rssi)+" dBm]" reception_stats += f" [RSSI {reception_rssi} dBm]"
if reception_snr != None: if reception_snr != None:
reception_stats += " [SNR "+str(reception_snr)+" dBm]" reception_stats += f" [SNR {reception_snr} dBm]"
else: else:
if packet.rssi != None: if packet.rssi != None:
reception_stats += " [RSSI "+str(packet.rssi)+" dBm]" reception_stats += f" [RSSI {packet.rssi} dBm]"
if packet.snr != None: if packet.snr != None:
reception_stats += " [SNR "+str(packet.snr)+" dB]" reception_stats += f" [SNR {packet.snr} dB]"
RNS.log("Received packet from echo client, proof sent"+reception_stats) RNS.log(f"Received packet from echo client, proof sent{reception_stats}")
########################################################## ##########################################################
@ -134,13 +132,13 @@ def client(destination_hexhash, configpath, timeout=None):
dest_len = (RNS.Reticulum.TRUNCATED_HASHLENGTH//8)*2 dest_len = (RNS.Reticulum.TRUNCATED_HASHLENGTH//8)*2
if len(destination_hexhash) != dest_len: if len(destination_hexhash) != dest_len:
raise ValueError( raise ValueError(
"Destination length is invalid, must be {hex} hexadecimal characters ({byte} bytes).".format(hex=dest_len, byte=dest_len//2) f"Destination length is invalid, must be {dest_len} hexadecimal characters ({dest_len // 2} bytes)."
) )
destination_hash = bytes.fromhex(destination_hexhash) destination_hash = bytes.fromhex(destination_hexhash)
except Exception as e: except Exception as e:
RNS.log("Invalid destination entered. Check your input!") RNS.log("Invalid destination entered. Check your input!")
RNS.log(str(e)+"\n") RNS.log(f"{e}\n")
exit() exit()
# We must first initialise Reticulum # We must first initialise Reticulum
@ -153,9 +151,7 @@ def client(destination_hexhash, configpath, timeout=None):
# Tell the user that the client is ready! # Tell the user that the client is ready!
RNS.log( RNS.log(
"Echo client ready, hit enter to send echo request to "+ f"Echo client ready, hit enter to send echo request to {destination_hexhash} (Ctrl-C to quit)"
destination_hexhash+
" (Ctrl-C to quit)"
) )
# We enter a loop that runs until the user exits. # We enter a loop that runs until the user exits.
@ -217,7 +213,7 @@ def client(destination_hexhash, configpath, timeout=None):
packet_receipt.set_delivery_callback(packet_delivered) packet_receipt.set_delivery_callback(packet_delivered)
# Tell the user that the echo request was sent # Tell the user that the echo request was sent
RNS.log("Sent echo request to "+RNS.prettyhexrep(request_destination.hash)) RNS.log(f"Sent echo request to {RNS.prettyhexrep(request_destination.hash)}")
else: else:
# If we do not know this destination, tell the # If we do not know this destination, tell the
# user to wait for an announce to arrive. # user to wait for an announce to arrive.
@ -234,10 +230,10 @@ def packet_delivered(receipt):
rtt = receipt.get_rtt() rtt = receipt.get_rtt()
if (rtt >= 1): if (rtt >= 1):
rtt = round(rtt, 3) rtt = round(rtt, 3)
rttstring = str(rtt)+" seconds" rttstring = f"{rtt} seconds"
else: else:
rtt = round(rtt*1000, 3) rtt = round(rtt*1000, 3)
rttstring = str(rtt)+" milliseconds" rttstring = f"{rtt} milliseconds"
reception_stats = "" reception_stats = ""
if reticulum.is_connected_to_shared_instance: if reticulum.is_connected_to_shared_instance:
@ -245,30 +241,27 @@ def packet_delivered(receipt):
reception_snr = reticulum.get_packet_snr(receipt.proof_packet.packet_hash) reception_snr = reticulum.get_packet_snr(receipt.proof_packet.packet_hash)
if reception_rssi != None: if reception_rssi != None:
reception_stats += " [RSSI "+str(reception_rssi)+" dBm]" reception_stats += f" [RSSI {reception_rssi} dBm]"
if reception_snr != None: if reception_snr != None:
reception_stats += " [SNR "+str(reception_snr)+" dB]" reception_stats += f" [SNR {reception_snr} dB]"
else: else:
if receipt.proof_packet != None: if receipt.proof_packet != None:
if receipt.proof_packet.rssi != None: if receipt.proof_packet.rssi != None:
reception_stats += " [RSSI "+str(receipt.proof_packet.rssi)+" dBm]" reception_stats += f" [RSSI {receipt.proof_packet.rssi} dBm]"
if receipt.proof_packet.snr != None: if receipt.proof_packet.snr != None:
reception_stats += " [SNR "+str(receipt.proof_packet.snr)+" dB]" reception_stats += f" [SNR {receipt.proof_packet.snr} dB]"
RNS.log( RNS.log(
"Valid reply received from "+ f"Valid reply received from {RNS.prettyhexrep(receipt.destination.hash)}, round-trip time is {rttstring}{reception_stats}"
RNS.prettyhexrep(receipt.destination.hash)+
", round-trip time is "+rttstring+
reception_stats
) )
# This function is called if a packet times out. # This function is called if a packet times out.
def packet_timed_out(receipt): def packet_timed_out(receipt):
if receipt.status == RNS.PacketReceipt.FAILED: if receipt.status == RNS.PacketReceipt.FAILED:
RNS.log("Packet "+RNS.prettyhexrep(receipt.hash)+" timed out") RNS.log(f"Packet {RNS.prettyhexrep(receipt.hash)} timed out")
########################################################## ##########################################################

View File

@ -24,7 +24,7 @@ APP_NAME = "example_utilities"
latest_client_link = None latest_client_link = None
def random_text_generator(path, data, request_id, link_id, remote_identity, requested_at): def random_text_generator(path, data, request_id, link_id, remote_identity, requested_at):
RNS.log("Generating response to request "+RNS.prettyhexrep(request_id)+" on link "+RNS.prettyhexrep(link_id)) RNS.log(f"Generating response to request {RNS.prettyhexrep(request_id)} on link {RNS.prettyhexrep(link_id)}")
texts = ["They looked up", "On each full moon", "Becky was upset", "Ill stay away from it", "The pet shop stocks everything"] texts = ["They looked up", "On each full moon", "Becky was upset", "Ill stay away from it", "The pet shop stocks everything"]
return texts[random.randint(0, len(texts)-1)] return texts[random.randint(0, len(texts)-1)]
@ -67,9 +67,7 @@ def server(configpath):
def server_loop(destination): def server_loop(destination):
# Let the user know that everything is ready # Let the user know that everything is ready
RNS.log( RNS.log(
"Request example "+ f"Request example {RNS.prettyhexrep(destination.hash)} running, waiting for a connection."
RNS.prettyhexrep(destination.hash)+
" running, waiting for a connection."
) )
RNS.log("Hit enter to manually send an announce (Ctrl-C to quit)") RNS.log("Hit enter to manually send an announce (Ctrl-C to quit)")
@ -81,7 +79,7 @@ def server_loop(destination):
while True: while True:
entered = input() entered = input()
destination.announce() destination.announce()
RNS.log("Sent announce from "+RNS.prettyhexrep(destination.hash)) RNS.log(f"Sent announce from {RNS.prettyhexrep(destination.hash)}")
# When a client establishes a link to our server # When a client establishes a link to our server
# destination, this function will be called with # destination, this function will be called with
@ -113,7 +111,7 @@ def client(destination_hexhash, configpath):
dest_len = (RNS.Reticulum.TRUNCATED_HASHLENGTH//8)*2 dest_len = (RNS.Reticulum.TRUNCATED_HASHLENGTH//8)*2
if len(destination_hexhash) != dest_len: if len(destination_hexhash) != dest_len:
raise ValueError( raise ValueError(
"Destination length is invalid, must be {hex} hexadecimal characters ({byte} bytes).".format(hex=dest_len, byte=dest_len//2) f"Destination length is invalid, must be {dest_len} hexadecimal characters ({dest_len // 2} bytes)."
) )
destination_hash = bytes.fromhex(destination_hexhash) destination_hash = bytes.fromhex(destination_hexhash)
@ -187,7 +185,7 @@ def client_loop():
except Exception as e: except Exception as e:
RNS.log("Error while sending request over the link: "+str(e)) RNS.log(f"Error while sending request over the link: {e}")
should_quit = True should_quit = True
server_link.teardown() server_link.teardown()
@ -195,13 +193,13 @@ def got_response(request_receipt):
request_id = request_receipt.request_id request_id = request_receipt.request_id
response = request_receipt.response response = request_receipt.response
RNS.log("Got response for request "+RNS.prettyhexrep(request_id)+": "+str(response)) RNS.log(f"Got response for request {RNS.prettyhexrep(request_id)}: {response}")
def request_received(request_receipt): def request_received(request_receipt):
RNS.log("The request "+RNS.prettyhexrep(request_receipt.request_id)+" was received by the remote peer.") RNS.log(f"The request {RNS.prettyhexrep(request_receipt.request_id)} was received by the remote peer.")
def request_failed(request_receipt): def request_failed(request_receipt):
RNS.log("The request "+RNS.prettyhexrep(request_receipt.request_id)+" failed.") RNS.log(f"The request {RNS.prettyhexrep(request_receipt.request_id)} failed.")
# This function is called when a link # This function is called when a link

View File

@ -62,9 +62,7 @@ def server(configpath):
def server_loop(destination): def server_loop(destination):
# Let the user know that everything is ready # Let the user know that everything is ready
RNS.log( RNS.log(
"Speedtest "+ f"Speedtest {RNS.prettyhexrep(destination.hash)} running, waiting for a connection."
RNS.prettyhexrep(destination.hash)+
" running, waiting for a connection."
) )
RNS.log("Hit enter to manually send an announce (Ctrl-C to quit)") RNS.log("Hit enter to manually send an announce (Ctrl-C to quit)")
@ -76,7 +74,7 @@ def server_loop(destination):
while True: while True:
entered = input() entered = input()
destination.announce() destination.announce()
RNS.log("Sent announce from "+RNS.prettyhexrep(destination.hash)) RNS.log(f"Sent announce from {RNS.prettyhexrep(destination.hash)}")
# When a client establishes a link to our server # When a client establishes a link to our server
# destination, this function will be called with # destination, this function will be called with
@ -108,9 +106,9 @@ def size_str(num, suffix='B'):
for unit in units: for unit in units:
if abs(num) < 1024.0: if abs(num) < 1024.0:
return "%3.2f %s%s" % (num, unit, suffix) return f"{num:3.2f} {unit}{suffix}"
num /= 1024.0 num /= 1024.0
return "%.2f %s%s" % (num, last_unit, suffix) return f"{num:.2f} {last_unit}{suffix}"
def server_packet_received(message, packet): def server_packet_received(message, packet):
@ -134,14 +132,14 @@ def server_packet_received(message, packet):
download_time = last_packet_at-first_packet_at download_time = last_packet_at-first_packet_at
hours, rem = divmod(download_time, 3600) hours, rem = divmod(download_time, 3600)
minutes, seconds = divmod(rem, 60) minutes, seconds = divmod(rem, 60)
timestring = "{:0>2}:{:0>2}:{:05.2f}".format(int(hours),int(minutes),seconds) timestring = f"{int(hours):0>2}:{int(minutes):0>2}:{seconds:05.2f}"
print("") print("")
print("") print("")
print("--- Statistics -----") print("--- Statistics -----")
print("\tTime taken : "+timestring) print(f"\tTime taken : {timestring}")
print("\tData transferred : "+size_str(rcv_d)) print(f"\tData transferred : {size_str(rcv_d)}")
print("\tTransfer rate : "+size_str(rcv_d/download_time, suffix='b')+"/s") print(f"\tTransfer rate : {size_str(rcv_d / download_time, suffix='b')}/s")
print("") print("")
sys.stdout.flush() sys.stdout.flush()
@ -169,7 +167,7 @@ def client(destination_hexhash, configpath):
dest_len = (RNS.Reticulum.TRUNCATED_HASHLENGTH//8)*2 dest_len = (RNS.Reticulum.TRUNCATED_HASHLENGTH//8)*2
if len(destination_hexhash) != dest_len: if len(destination_hexhash) != dest_len:
raise ValueError( raise ValueError(
"Destination length is invalid, must be {hex} hexadecimal characters ({byte} bytes).".format(hex=dest_len, byte=dest_len//2) f"Destination length is invalid, must be {dest_len} hexadecimal characters ({dest_len // 2} bytes)."
) )
destination_hash = bytes.fromhex(destination_hexhash) destination_hash = bytes.fromhex(destination_hexhash)
@ -260,13 +258,13 @@ def link_established(link):
download_time = ended-started download_time = ended-started
hours, rem = divmod(download_time, 3600) hours, rem = divmod(download_time, 3600)
minutes, seconds = divmod(rem, 60) minutes, seconds = divmod(rem, 60)
timestring = "{:0>2}:{:0>2}:{:05.2f}".format(int(hours),int(minutes),seconds) timestring = f"{int(hours):0>2}:{int(minutes):0>2}:{seconds:05.2f}"
print("") print("")
print("") print("")
print("--- Statistics -----") print("--- Statistics -----")
print("\tTime taken : "+timestring) print(f"\tTime taken : {timestring}")
print("\tData transferred : "+size_str(data_sent)) print(f"\tData transferred : {size_str(data_sent)}")
print("\tTransfer rate : "+size_str(data_sent/download_time, suffix='b')+"/s") print(f"\tTransfer rate : {size_str(data_sent / download_time, suffix='b')}/s")
print("") print("")
sys.stdout.flush() sys.stdout.flush()

View File

@ -65,7 +65,7 @@ class StreamDataMessage(MessageBase):
raise ValueError("stream_id must be 0-16383") raise ValueError("stream_id must be 0-16383")
self.stream_id = stream_id self.stream_id = stream_id
self.compressed = compressed self.compressed = compressed
self.data = data or bytes() self.data = data or b''
self.eof = eof self.eof = eof
def pack(self) -> bytes: def pack(self) -> bytes:
@ -73,7 +73,7 @@ class StreamDataMessage(MessageBase):
raise ValueError("stream_id") raise ValueError("stream_id")
header_val = (0x3fff & self.stream_id) | (0x8000 if self.eof else 0x0000) | (0x4000 if self.compressed > 0 else 0x0000) header_val = (0x3fff & self.stream_id) | (0x8000 if self.eof else 0x0000) | (0x4000 if self.compressed > 0 else 0x0000)
return bytes(struct.pack(">H", header_val) + (self.data if self.data else bytes())) return bytes(struct.pack(">H", header_val) + (self.data if self.data else b''))
def unpack(self, raw): def unpack(self, raw):
self.stream_id = struct.unpack(">H", raw[:2])[0] self.stream_id = struct.unpack(">H", raw[:2])[0]
@ -148,7 +148,7 @@ class RawChannelReader(RawIOBase, AbstractContextManager):
try: try:
threading.Thread(target=listener, name="Message Callback", args=[len(self._buffer)], daemon=True).start() threading.Thread(target=listener, name="Message Callback", args=[len(self._buffer)], daemon=True).start()
except Exception as ex: except Exception as ex:
RNS.log("Error calling RawChannelReader(" + str(self._stream_id) + ") callback: " + str(ex), RNS.LOG_ERROR) RNS.log(f"Error calling RawChannelReader({self._stream_id}) callback: {ex}", RNS.LOG_ERROR)
return True return True
return False return False
@ -264,7 +264,7 @@ class RawChannelWriter(RawIOBase, AbstractContextManager):
time.sleep(0.05) time.sleep(0.05)
self._eof = True self._eof = True
self.write(bytes()) self.write(b'')
def __enter__(self): def __enter__(self):
return self return self

View File

@ -168,7 +168,7 @@ class Envelope:
Internal wrapper used to transport messages over a channel and Internal wrapper used to transport messages over a channel and
track its state within the channel framework. track its state within the channel framework.
""" """
def unpack(self, message_factories: dict[int, Type]) -> MessageBase: def unpack(self, message_factories: dict[int, type]) -> MessageBase:
msgtype, self.sequence, length = struct.unpack(">HHH", self.raw[:6]) msgtype, self.sequence, length = struct.unpack(">HHH", self.raw[:6])
raw = self.raw[6:] raw = self.raw[6:]
ctor = message_factories.get(msgtype, None) ctor = message_factories.get(msgtype, None)
@ -282,7 +282,7 @@ class Channel(contextlib.AbstractContextManager):
self._message_callbacks: [MessageCallbackType] = [] self._message_callbacks: [MessageCallbackType] = []
self._next_sequence = 0 self._next_sequence = 0
self._next_rx_sequence = 0 self._next_rx_sequence = 0
self._message_factories: dict[int, Type[MessageBase]] = {} self._message_factories: dict[int, type[MessageBase]] = {}
self._max_tries = 5 self._max_tries = 5
self.fast_rate_rounds = 0 self.fast_rate_rounds = 0
self.medium_rate_rounds = 0 self.medium_rate_rounds = 0
@ -301,12 +301,12 @@ class Channel(contextlib.AbstractContextManager):
def __enter__(self) -> Channel: def __enter__(self) -> Channel:
return self return self
def __exit__(self, __exc_type: Type[BaseException] | None, __exc_value: BaseException | None, def __exit__(self, __exc_type: type[BaseException] | None, __exc_value: BaseException | None,
__traceback: TracebackType | None) -> bool | None: __traceback: TracebackType | None) -> bool | None:
self._shutdown() self._shutdown()
return False return False
def register_message_type(self, message_class: Type[MessageBase]): def register_message_type(self, message_class: type[MessageBase]):
""" """
Register a message class for reception over a ``Channel``. Register a message class for reception over a ``Channel``.
@ -316,7 +316,7 @@ class Channel(contextlib.AbstractContextManager):
""" """
self._register_message_type(message_class, is_system_type=False) self._register_message_type(message_class, is_system_type=False)
def _register_message_type(self, message_class: Type[MessageBase], *, is_system_type: bool = False): def _register_message_type(self, message_class: type[MessageBase], *, is_system_type: bool = False):
with self._lock: with self._lock:
if not issubclass(message_class, MessageBase): if not issubclass(message_class, MessageBase):
raise ChannelException(CEType.ME_INVALID_MSG_TYPE, raise ChannelException(CEType.ME_INVALID_MSG_TYPE,
@ -384,7 +384,7 @@ class Channel(contextlib.AbstractContextManager):
for existing in ring: for existing in ring:
if envelope.sequence == existing.sequence: if envelope.sequence == existing.sequence:
RNS.log(f"Envelope: Emplacement of duplicate envelope with sequence "+str(envelope.sequence), RNS.LOG_EXTREME) RNS.log(f"Envelope: Emplacement of duplicate envelope with sequence {envelope.sequence}", RNS.LOG_EXTREME)
return False return False
if envelope.sequence < existing.sequence and not (self._next_rx_sequence - envelope.sequence) > (Channel.SEQ_MAX//2): if envelope.sequence < existing.sequence and not (self._next_rx_sequence - envelope.sequence) > (Channel.SEQ_MAX//2):
@ -408,7 +408,7 @@ class Channel(contextlib.AbstractContextManager):
if cb(message): if cb(message):
return return
except Exception as e: except Exception as e:
RNS.log("Channel "+str(self)+" experienced an error while running a message callback. The contained exception was: "+str(e), RNS.LOG_ERROR) RNS.log(f"Channel {self} experienced an error while running a message callback. The contained exception was: {e}", RNS.LOG_ERROR)
def _receive(self, raw: bytes): def _receive(self, raw: bytes):
try: try:
@ -420,16 +420,16 @@ class Channel(contextlib.AbstractContextManager):
window_overflow = (self._next_rx_sequence+Channel.WINDOW_MAX) % Channel.SEQ_MODULUS window_overflow = (self._next_rx_sequence+Channel.WINDOW_MAX) % Channel.SEQ_MODULUS
if window_overflow < self._next_rx_sequence: if window_overflow < self._next_rx_sequence:
if envelope.sequence > window_overflow: if envelope.sequence > window_overflow:
RNS.log("Invalid packet sequence ("+str(envelope.sequence)+") received on channel "+str(self), RNS.LOG_EXTREME) RNS.log(f"Invalid packet sequence ({envelope.sequence}) received on channel {self}", RNS.LOG_EXTREME)
return return
else: else:
RNS.log("Invalid packet sequence ("+str(envelope.sequence)+") received on channel "+str(self), RNS.LOG_EXTREME) RNS.log(f"Invalid packet sequence ({envelope.sequence}) received on channel {self}", RNS.LOG_EXTREME)
return return
is_new = self._emplace_envelope(envelope, self._rx_ring) is_new = self._emplace_envelope(envelope, self._rx_ring)
if not is_new: if not is_new:
RNS.log("Duplicate message received on channel "+str(self), RNS.LOG_EXTREME) RNS.log(f"Duplicate message received on channel {self}", RNS.LOG_EXTREME)
return return
else: else:
with self._lock: with self._lock:
@ -454,7 +454,7 @@ class Channel(contextlib.AbstractContextManager):
self._run_callbacks(m) self._run_callbacks(m)
except Exception as e: except Exception as e:
RNS.log("An error ocurred while receiving data on "+str(self)+". The contained exception was: "+str(e), RNS.LOG_ERROR) RNS.log(f"An error ocurred while receiving data on {self}. The contained exception was: {e}", RNS.LOG_ERROR)
def is_ready_to_send(self) -> bool: def is_ready_to_send(self) -> bool:
""" """
@ -520,9 +520,9 @@ class Channel(contextlib.AbstractContextManager):
else: else:
RNS.log("Envelope not found in TX ring for "+str(self), RNS.LOG_EXTREME) RNS.log(f"Envelope not found in TX ring for {self}", RNS.LOG_EXTREME)
if not envelope: if not envelope:
RNS.log("Spurious message received on "+str(self), RNS.LOG_EXTREME) RNS.log(f"Spurious message received on {self}", RNS.LOG_EXTREME)
def _packet_delivered(self, packet: TPacket): def _packet_delivered(self, packet: TPacket):
self._packet_tx_op(packet, lambda env: True) self._packet_tx_op(packet, lambda env: True)
@ -541,7 +541,7 @@ class Channel(contextlib.AbstractContextManager):
def _packet_timeout(self, packet: TPacket): def _packet_timeout(self, packet: TPacket):
def retry_envelope(envelope: Envelope) -> bool: def retry_envelope(envelope: Envelope) -> bool:
if envelope.tries >= self._max_tries: if envelope.tries >= self._max_tries:
RNS.log("Retry count exceeded on "+str(self)+", tearing down Link.", RNS.LOG_ERROR) RNS.log(f"Retry count exceeded on {self}, tearing down Link.", RNS.LOG_ERROR)
self._shutdown() # start on separate thread? self._shutdown() # start on separate thread?
self._outlet.timed_out() self._outlet.timed_out()
return True return True

View File

@ -48,7 +48,7 @@ class Fernet():
raise ValueError("Token key cannot be None") raise ValueError("Token key cannot be None")
if len(key) != 32: if len(key) != 32:
raise ValueError("Token key must be 32 bytes, not "+str(len(key))) raise ValueError(f"Token key must be 32 bytes, not {len(key)}")
self._signing_key = key[:16] self._signing_key = key[:16]
self._encryption_key = key[16:] self._encryption_key = key[16:]
@ -56,7 +56,7 @@ class Fernet():
def verify_hmac(self, token): def verify_hmac(self, token):
if len(token) <= 32: if len(token) <= 32:
raise ValueError("Cannot verify HMAC on token of only "+str(len(token))+" bytes") raise ValueError(f"Cannot verify HMAC on token of only {len(token)} bytes")
else: else:
received_hmac = token[-32:] received_hmac = token[-32:]
expected_hmac = HMAC.new(self._signing_key, token[:-32]).digest() expected_hmac = HMAC.new(self._signing_key, token[:-32]).digest()

View File

@ -37,7 +37,7 @@ class HMAC:
""" """
if not isinstance(key, (bytes, bytearray)): if not isinstance(key, (bytes, bytearray)):
raise TypeError("key: expected bytes or bytearray, but got %r" % type(key).__name__) raise TypeError(f"key: expected bytes or bytearray, but got {type(key).__name__!r}")
if not digestmod: if not digestmod:
raise TypeError("Missing required parameter 'digestmod'.") raise TypeError("Missing required parameter 'digestmod'.")
@ -60,13 +60,11 @@ class HMAC:
if hasattr(self._inner, 'block_size'): if hasattr(self._inner, 'block_size'):
blocksize = self._inner.block_size blocksize = self._inner.block_size
if blocksize < 16: if blocksize < 16:
_warnings.warn('block_size of %d seems too small; using our ' _warnings.warn(f'block_size of {int(blocksize)} seems too small; using our default of {int(self.blocksize)}.',
'default of %d.' % (blocksize, self.blocksize),
RuntimeWarning, 2) RuntimeWarning, 2)
blocksize = self.blocksize blocksize = self.blocksize
else: else:
_warnings.warn('No block_size attribute on given digest object; ' _warnings.warn(f'No block_size attribute on given digest object; Assuming {int(self.blocksize)}.',
'Assuming %d.' % (self.blocksize),
RuntimeWarning, 2) RuntimeWarning, 2)
blocksize = self.blocksize blocksize = self.blocksize

View File

@ -35,6 +35,6 @@ class PKCS7:
l = len(data) l = len(data)
n = data[-1] n = data[-1]
if n > bs: if n > bs:
raise ValueError("Cannot unpad, invalid padding length of "+str(n)+" bytes") raise ValueError(f"Cannot unpad, invalid padding length of {n} bytes")
else: else:
return data[:l-n] return data[:l-n]

View File

@ -35,4 +35,4 @@ def backend():
elif PROVIDER == PROVIDER_INTERNAL: elif PROVIDER == PROVIDER_INTERNAL:
return "internal" return "internal"
elif PROVIDER == PROVIDER_PYCA: elif PROVIDER == PROVIDER_PYCA:
return "openssl, PyCA "+str(pyca_v) return f"openssl, PyCA {pyca_v}"

View File

@ -28,7 +28,7 @@ import sys
def new(m=None): def new(m=None):
return sha256(m) return sha256(m)
class sha256(object): class sha256:
_k = (0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, _k = (0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5,
0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3,
@ -59,7 +59,7 @@ class sha256(object):
if m is not None: if m is not None:
if type(m) is not bytes: if type(m) is not bytes:
raise TypeError('%s() argument 1 must be bytes, not %s' % (self.__class__.__name__, type(m).__name__)) raise TypeError(f'{self.__class__.__name__}() argument 1 must be bytes, not {type(m).__name__}')
self.update(m) self.update(m)
def _rotr(self, x, y): def _rotr(self, x, y):
@ -100,7 +100,7 @@ class sha256(object):
return return
if type(m) is not bytes: if type(m) is not bytes:
raise TypeError('%s() argument 1 must be bytes, not %s' % (sys._getframe().f_code.co_name, type(m).__name__)) raise TypeError(f'{sys._getframe().f_code.co_name}() argument 1 must be bytes, not {type(m).__name__}')
self._buffer += m self._buffer += m
self._counter += len(m) self._counter += len(m)

View File

@ -25,7 +25,7 @@ import copy, struct, sys
def new(m=None): def new(m=None):
return sha512(m) return sha512(m)
class sha512(object): class sha512:
_k = (0x428a2f98d728ae22, 0x7137449123ef65cd, 0xb5c0fbcfec4d3b2f, 0xe9b5dba58189dbbc, _k = (0x428a2f98d728ae22, 0x7137449123ef65cd, 0xb5c0fbcfec4d3b2f, 0xe9b5dba58189dbbc,
0x3956c25bf348b538, 0x59f111f1b605d019, 0x923f82a4af194f9b, 0xab1c5ed5da6d8118, 0x3956c25bf348b538, 0x59f111f1b605d019, 0x923f82a4af194f9b, 0xab1c5ed5da6d8118,
0xd807aa98a3030242, 0x12835b0145706fbe, 0x243185be4ee4b28c, 0x550c7dc3d5ffb4e2, 0xd807aa98a3030242, 0x12835b0145706fbe, 0x243185be4ee4b28c, 0x550c7dc3d5ffb4e2,
@ -60,7 +60,7 @@ class sha512(object):
if m is not None: if m is not None:
if type(m) is not bytes: if type(m) is not bytes:
raise TypeError('%s() argument 1 must be bytes, not %s' % (self.__class__.__name__, type(m).__name__)) raise TypeError(f'{self.__class__.__name__}() argument 1 must be bytes, not {type(m).__name__}')
self.update(m) self.update(m)
def _rotr(self, x, y): def _rotr(self, x, y):
@ -100,7 +100,7 @@ class sha512(object):
if not m: if not m:
return return
if type(m) is not bytes: if type(m) is not bytes:
raise TypeError('%s() argument 1 must be bytes, not %s' % (sys._getframe().f_code.co_name, type(m).__name__)) raise TypeError(f'{sys._getframe().f_code.co_name}() argument 1 must be bytes, not {type(m).__name__}')
self._buffer += m self._buffer += m
self._counter += len(m) self._counter += len(m)

View File

@ -20,5 +20,5 @@ elif cp.PROVIDER == cp.PROVIDER_PYCA:
from RNS.Cryptography.Proxies import Ed25519PrivateKeyProxy as Ed25519PrivateKey from RNS.Cryptography.Proxies import Ed25519PrivateKeyProxy as Ed25519PrivateKey
from RNS.Cryptography.Proxies import Ed25519PublicKeyProxy as Ed25519PublicKey from RNS.Cryptography.Proxies import Ed25519PublicKeyProxy as Ed25519PublicKey
modules = glob.glob(os.path.dirname(__file__)+"/*.py") modules = glob.glob(f"{os.path.dirname(__file__)}/*.py")
__all__ = [ os.path.basename(f)[:-3] for f in modules if not f.endswith('__init__.py')] __all__ = [ os.path.basename(f)[:-3] for f in modules if not f.endswith('__init__.py')]

View File

@ -230,7 +230,7 @@ def test():
print(f"Ciphertext Hex: {ciphertext_block.hex()}") print(f"Ciphertext Hex: {ciphertext_block.hex()}")
print(f"Plaintext: {plaintext_block.decode()}") print(f"Plaintext: {plaintext_block.decode()}")
assert plaintext_block == single_block_text assert plaintext_block == single_block_text
print(bcolors.OK + "Single Block Test Passed Successfully" + bcolors.RESET) print(f"{bcolors.OK}Single Block Test Passed Successfully{bcolors.RESET}")
print() print()
# test a less than a block length phrase # test a less than a block length phrase
@ -246,7 +246,7 @@ def test():
print(f"Ciphertext Hex: {ciphertext_short.hex()}") print(f"Ciphertext Hex: {ciphertext_short.hex()}")
print(f"Plaintext: {plaintext_short.decode()}") print(f"Plaintext: {plaintext_short.decode()}")
assert short_text == plaintext_short assert short_text == plaintext_short
print(bcolors.OK + "Short Text Test Passed Successfully" + bcolors.RESET) print(f"{bcolors.OK}Short Text Test Passed Successfully{bcolors.RESET}")
print() print()
# test an arbitrary length phrase # test an arbitrary length phrase
@ -262,7 +262,7 @@ def test():
print(f"Ciphertext Hex: {ciphertext.hex()}") print(f"Ciphertext Hex: {ciphertext.hex()}")
print(f"Plaintext: {plaintext.decode()}") print(f"Plaintext: {plaintext.decode()}")
assert text == plaintext assert text == plaintext
print(bcolors.OK + "Arbitrary Length Text Test Passed Successfully" + bcolors.RESET) print(f"{bcolors.OK}Arbitrary Length Text Test Passed Successfully{bcolors.RESET}")
print() print()

View File

@ -141,7 +141,7 @@ def encodepoint(P):
assert 0 <= y < (1<<255) # always < 0x7fff..ff assert 0 <= y < (1<<255) # always < 0x7fff..ff
if x & 1: if x & 1:
y += 1<<255 y += 1<<255
return binascii.unhexlify("%064x" % y)[::-1] return binascii.unhexlify(f"{y:064x}")[::-1]
def isoncurve(P): def isoncurve(P):
x = P[0] x = P[0]
@ -192,7 +192,7 @@ def password_to_scalar(pw):
def scalar_to_bytes(y): def scalar_to_bytes(y):
y = y % L y = y % L
assert 0 <= y < 2**256 assert 0 <= y < 2**256
return binascii.unhexlify("%064x" % y)[::-1] return binascii.unhexlify(f"{y:064x}")[::-1]
# Elements, of various orders # Elements, of various orders

View File

@ -39,7 +39,7 @@ class BadPrefixError(Exception):
def remove_prefix(s_bytes, prefix): def remove_prefix(s_bytes, prefix):
assert(type(s_bytes) == type(prefix)) assert(type(s_bytes) == type(prefix))
if s_bytes[:len(prefix)] != prefix: if s_bytes[:len(prefix)] != prefix:
raise BadPrefixError("did not see expected '%s' prefix" % (prefix,)) raise BadPrefixError(f"did not see expected '{prefix}' prefix")
return s_bytes[len(prefix):] return s_bytes[len(prefix):]
def to_ascii(s_bytes, prefix="", encoding="base64"): def to_ascii(s_bytes, prefix="", encoding="base64"):
@ -93,7 +93,7 @@ def from_ascii(s_ascii, prefix="", encoding="base64"):
raise NotImplementedError raise NotImplementedError
return s_bytes return s_bytes
class SigningKey(object): class SigningKey:
# this can only be used to reconstruct a key created by create_keypair(). # this can only be used to reconstruct a key created by create_keypair().
def __init__(self, sk_s, prefix="", encoding=None): def __init__(self, sk_s, prefix="", encoding=None):
assert isinstance(sk_s, bytes) assert isinstance(sk_s, bytes)
@ -150,7 +150,7 @@ class SigningKey(object):
return to_ascii(sig_out, prefix, encoding) return to_ascii(sig_out, prefix, encoding)
return prefix+sig_out return prefix+sig_out
class VerifyingKey(object): class VerifyingKey:
def __init__(self, vk_s, prefix="", encoding=None): def __init__(self, vk_s, prefix="", encoding=None):
if not isinstance(prefix, bytes): if not isinstance(prefix, bytes):
prefix = prefix.encode('ascii') prefix = prefix.encode('ascii')

View File

@ -78,16 +78,16 @@ def sign(skbytes, msg):
"""Return just the signature, given the message and just the secret """Return just the signature, given the message and just the secret
key.""" key."""
if len(skbytes) != 32: if len(skbytes) != 32:
raise ValueError("Bad signing key length %d" % len(skbytes)) raise ValueError(f"Bad signing key length {len(skbytes)}")
vkbytes = create_verifying_key(skbytes) vkbytes = create_verifying_key(skbytes)
sig = signature(msg, skbytes, vkbytes) sig = signature(msg, skbytes, vkbytes)
return sig return sig
def verify(vkbytes, sig, msg): def verify(vkbytes, sig, msg):
if len(vkbytes) != 32: if len(vkbytes) != 32:
raise ValueError("Bad verifying key length %d" % len(vkbytes)) raise ValueError(f"Bad verifying key length {len(vkbytes)}")
if len(sig) != 64: if len(sig) != 64:
raise ValueError("Bad signature length %d" % len(sig)) raise ValueError(f"Bad signature length {len(sig)}")
rc = checkvalid(sig, msg, vkbytes) rc = checkvalid(sig, msg, vkbytes)
if not rc: if not rc:
raise ValueError("rc != 0", rc) raise ValueError("rc != 0", rc)

View File

@ -96,10 +96,10 @@ class Destination:
name = app_name name = app_name
for aspect in aspects: for aspect in aspects:
if "." in aspect: raise ValueError("Dots can't be used in aspects") if "." in aspect: raise ValueError("Dots can't be used in aspects")
name += "." + aspect name += f".{aspect}"
if identity != None: if identity != None:
name += "." + identity.hexhash name += f".{identity.hexhash}"
return name return name
@ -192,7 +192,7 @@ class Destination:
""" """
:returns: A human-readable representation of the destination including addressable hash and full name. :returns: A human-readable representation of the destination including addressable hash and full name.
""" """
return "<"+self.name+"/"+self.hexhash+">" return f"<{self.name}/{self.hexhash}>"
def _clean_ratchets(self): def _clean_ratchets(self):
if self.ratchets != None: if self.ratchets != None:
@ -210,13 +210,13 @@ class Destination:
except Exception as e: except Exception as e:
self.ratchets = None self.ratchets = None
self.ratchets_path = None self.ratchets_path = None
raise OSError("Could not write ratchet file contents for "+str(self)+". The contained exception was: "+str(e), RNS.LOG_ERROR) raise OSError(f"Could not write ratchet file contents for {self}. The contained exception was: {e}", RNS.LOG_ERROR)
def rotate_ratchets(self): def rotate_ratchets(self):
if self.ratchets != None: if self.ratchets != None:
now = time.time() now = time.time()
if now > self.latest_ratchet_time+self.ratchet_interval: if now > self.latest_ratchet_time+self.ratchet_interval:
RNS.log("Rotating ratchets for "+str(self), RNS.LOG_DEBUG) RNS.log(f"Rotating ratchets for {self}", RNS.LOG_DEBUG)
new_ratchet = RNS.Identity._generate_ratchet() new_ratchet = RNS.Identity._generate_ratchet()
self.ratchets.insert(0, new_ratchet) self.ratchets.insert(0, new_ratchet)
self.latest_ratchet_time = now self.latest_ratchet_time = now
@ -224,7 +224,7 @@ class Destination:
self._persist_ratchets() self._persist_ratchets()
return True return True
else: else:
raise SystemError("Cannot rotate ratchet on "+str(self)+", ratchets are not enabled") raise SystemError(f"Cannot rotate ratchet on {self}, ratchets are not enabled")
return False return False
@ -262,7 +262,7 @@ class Destination:
# received via multiple paths. The difference in reception time will # received via multiple paths. The difference in reception time will
# potentially also be useful in determining characteristics of the # potentially also be useful in determining characteristics of the
# multiple available paths, and to choose the best one. # multiple available paths, and to choose the best one.
RNS.log("Using cached announce data for answering path request with tag "+RNS.prettyhexrep(tag), RNS.LOG_EXTREME) RNS.log(f"Using cached announce data for answering path request with tag {RNS.prettyhexrep(tag)}", RNS.LOG_EXTREME)
announce_data = self.path_responses[tag][1] announce_data = self.path_responses[tag][1]
else: else:
@ -413,7 +413,7 @@ class Destination:
try: try:
self.callbacks.packet(plaintext, packet) self.callbacks.packet(plaintext, packet)
except Exception as e: except Exception as e:
RNS.log("Error while executing receive callback from "+str(self)+". The contained exception was: "+str(e), RNS.LOG_ERROR) RNS.log(f"Error while executing receive callback from {self}. The contained exception was: {e}", RNS.LOG_ERROR)
def incoming_link_request(self, data, packet): def incoming_link_request(self, data, packet):
if self.accept_link_requests: if self.accept_link_requests:
@ -437,9 +437,9 @@ class Destination:
except Exception as e: except Exception as e:
self.ratchets = None self.ratchets = None
self.ratchets_path = None self.ratchets_path = None
raise OSError("Could not read ratchet file contents for "+str(self)+". The contained exception was: "+str(e), RNS.LOG_ERROR) raise OSError(f"Could not read ratchet file contents for {self}. The contained exception was: {e}", RNS.LOG_ERROR)
else: else:
RNS.log("No existing ratchet data found, initialising new ratchet file for "+str(self), RNS.LOG_DEBUG) RNS.log(f"No existing ratchet data found, initialising new ratchet file for {self}", RNS.LOG_DEBUG)
self.ratchets = [] self.ratchets = []
self.ratchets_path = ratchets_path self.ratchets_path = ratchets_path
self._persist_ratchets() self._persist_ratchets()
@ -464,11 +464,11 @@ class Destination:
self._reload_ratchets(ratchets_path) self._reload_ratchets(ratchets_path)
# TODO: Remove at some point # TODO: Remove at some point
RNS.log("Ratchets enabled on "+str(self), RNS.LOG_DEBUG) RNS.log(f"Ratchets enabled on {self}", RNS.LOG_DEBUG)
return True return True
else: else:
raise ValueError("No ratchet file path specified for "+str(self)) raise ValueError(f"No ratchet file path specified for {self}")
def enforce_ratchets(self): def enforce_ratchets(self):
""" """
@ -478,7 +478,7 @@ class Destination:
""" """
if self.ratchets != None: if self.ratchets != None:
self.__enforce_ratchets = True self.__enforce_ratchets = True
RNS.log("Ratchets enforced on "+str(self), RNS.LOG_DEBUG) RNS.log(f"Ratchets enforced on {self}", RNS.LOG_DEBUG)
return True return True
else: else:
return False return False
@ -586,7 +586,7 @@ class Destination:
return self.prv.encrypt(plaintext) return self.prv.encrypt(plaintext)
except Exception as e: except Exception as e:
RNS.log("The GROUP destination could not encrypt data", RNS.LOG_ERROR) RNS.log("The GROUP destination could not encrypt data", RNS.LOG_ERROR)
RNS.log("The contained exception was: "+str(e), RNS.LOG_ERROR) RNS.log(f"The contained exception was: {e}", RNS.LOG_ERROR)
else: else:
raise ValueError("No private key held by GROUP destination. Did you create or load one?") raise ValueError("No private key held by GROUP destination. Did you create or load one?")
@ -630,7 +630,7 @@ class Destination:
return self.prv.decrypt(ciphertext) return self.prv.decrypt(ciphertext)
except Exception as e: except Exception as e:
RNS.log("The GROUP destination could not decrypt data", RNS.LOG_ERROR) RNS.log("The GROUP destination could not decrypt data", RNS.LOG_ERROR)
RNS.log("The contained exception was: "+str(e), RNS.LOG_ERROR) RNS.log(f"The contained exception was: {e}", RNS.LOG_ERROR)
else: else:
raise ValueError("No private key held by GROUP destination. Did you create or load one?") raise ValueError("No private key held by GROUP destination. Did you create or load one?")

View File

@ -87,7 +87,7 @@ class Identity:
@staticmethod @staticmethod
def remember(packet_hash, destination_hash, public_key, app_data = None): def remember(packet_hash, destination_hash, public_key, app_data = None):
if len(public_key) != Identity.KEYSIZE//8: if len(public_key) != Identity.KEYSIZE//8:
raise TypeError("Can't remember "+RNS.prettyhexrep(destination_hash)+", the public key size of "+str(len(public_key))+" is not valid.", RNS.LOG_ERROR) raise TypeError(f"Can't remember {RNS.prettyhexrep(destination_hash)}, the public key size of {len(public_key)} is not valid.", RNS.LOG_ERROR)
else: else:
Identity.known_destinations[destination_hash] = [time.time(), packet_hash, public_key, app_data] Identity.known_destinations[destination_hash] = [time.time(), packet_hash, public_key, app_data]
@ -153,9 +153,9 @@ class Identity:
save_start = time.time() save_start = time.time()
storage_known_destinations = {} storage_known_destinations = {}
if os.path.isfile(RNS.Reticulum.storagepath+"/known_destinations"): if os.path.isfile(f"{RNS.Reticulum.storagepath}/known_destinations"):
try: try:
file = open(RNS.Reticulum.storagepath+"/known_destinations","rb") file = open(f"{RNS.Reticulum.storagepath}/known_destinations","rb")
storage_known_destinations = umsgpack.load(file) storage_known_destinations = umsgpack.load(file)
file.close() file.close()
except: except:
@ -166,32 +166,32 @@ class Identity:
if not destination_hash in Identity.known_destinations: if not destination_hash in Identity.known_destinations:
Identity.known_destinations[destination_hash] = storage_known_destinations[destination_hash] Identity.known_destinations[destination_hash] = storage_known_destinations[destination_hash]
except Exception as e: except Exception as e:
RNS.log("Skipped recombining known destinations from disk, since an error occurred: "+str(e), RNS.LOG_WARNING) RNS.log(f"Skipped recombining known destinations from disk, since an error occurred: {e}", RNS.LOG_WARNING)
RNS.log("Saving "+str(len(Identity.known_destinations))+" known destinations to storage...", RNS.LOG_DEBUG) RNS.log(f"Saving {len(Identity.known_destinations)} known destinations to storage...", RNS.LOG_DEBUG)
file = open(RNS.Reticulum.storagepath+"/known_destinations","wb") file = open(f"{RNS.Reticulum.storagepath}/known_destinations","wb")
umsgpack.dump(Identity.known_destinations, file) umsgpack.dump(Identity.known_destinations, file)
file.close() file.close()
save_time = time.time() - save_start save_time = time.time() - save_start
if save_time < 1: if save_time < 1:
time_str = str(round(save_time*1000,2))+"ms" time_str = f"{round(save_time * 1000, 2)}ms"
else: else:
time_str = str(round(save_time,2))+"s" time_str = f"{round(save_time, 2)}s"
RNS.log("Saved known destinations to storage in "+time_str, RNS.LOG_DEBUG) RNS.log(f"Saved known destinations to storage in {time_str}", RNS.LOG_DEBUG)
except Exception as e: except Exception as e:
RNS.log("Error while saving known destinations to disk, the contained exception was: "+str(e), RNS.LOG_ERROR) RNS.log(f"Error while saving known destinations to disk, the contained exception was: {e}", RNS.LOG_ERROR)
RNS.trace_exception(e) RNS.trace_exception(e)
Identity.saving_known_destinations = False Identity.saving_known_destinations = False
@staticmethod @staticmethod
def load_known_destinations(): def load_known_destinations():
if os.path.isfile(RNS.Reticulum.storagepath+"/known_destinations"): if os.path.isfile(f"{RNS.Reticulum.storagepath}/known_destinations"):
try: try:
file = open(RNS.Reticulum.storagepath+"/known_destinations","rb") file = open(f"{RNS.Reticulum.storagepath}/known_destinations","rb")
loaded_known_destinations = umsgpack.load(file) loaded_known_destinations = umsgpack.load(file)
file.close() file.close()
@ -200,7 +200,7 @@ class Identity:
if len(known_destination) == RNS.Reticulum.TRUNCATED_HASHLENGTH//8: if len(known_destination) == RNS.Reticulum.TRUNCATED_HASHLENGTH//8:
Identity.known_destinations[known_destination] = loaded_known_destinations[known_destination] Identity.known_destinations[known_destination] = loaded_known_destinations[known_destination]
RNS.log("Loaded "+str(len(Identity.known_destinations))+" known destination from storage", RNS.LOG_VERBOSE) RNS.log(f"Loaded {len(Identity.known_destinations)} known destination from storage", RNS.LOG_VERBOSE)
except Exception as e: except Exception as e:
RNS.log("Error loading known destinations from disk, file will be recreated on exit", RNS.LOG_ERROR) RNS.log("Error loading known destinations from disk, file will be recreated on exit", RNS.LOG_ERROR)
@ -278,7 +278,7 @@ class Identity:
hexhash = RNS.hexrep(destination_hash, delimit=False) hexhash = RNS.hexrep(destination_hash, delimit=False)
ratchet_data = {"ratchet": ratchet, "received": time.time()} ratchet_data = {"ratchet": ratchet, "received": time.time()}
ratchetdir = RNS.Reticulum.storagepath+"/ratchets" ratchetdir = f"{RNS.Reticulum.storagepath}/ratchets"
if not os.path.isdir(ratchetdir): if not os.path.isdir(ratchetdir):
os.makedirs(ratchetdir) os.makedirs(ratchetdir)
@ -303,7 +303,7 @@ class Identity:
RNS.log("Cleaning ratchets...", RNS.LOG_DEBUG) RNS.log("Cleaning ratchets...", RNS.LOG_DEBUG)
try: try:
now = time.time() now = time.time()
ratchetdir = RNS.Reticulum.storagepath+"/ratchets" ratchetdir = f"{RNS.Reticulum.storagepath}/ratchets"
if os.path.isdir(ratchetdir): if os.path.isdir(ratchetdir):
for filename in os.listdir(ratchetdir): for filename in os.listdir(ratchetdir):
try: try:
@ -326,7 +326,7 @@ class Identity:
@staticmethod @staticmethod
def get_ratchet(destination_hash): def get_ratchet(destination_hash):
if not destination_hash in Identity.known_ratchets: if not destination_hash in Identity.known_ratchets:
ratchetdir = RNS.Reticulum.storagepath+"/ratchets" ratchetdir = f"{RNS.Reticulum.storagepath}/ratchets"
hexhash = RNS.hexrep(destination_hash, delimit=False) hexhash = RNS.hexrep(destination_hash, delimit=False)
ratchet_path = f"{ratchetdir}/{hexhash}" ratchet_path = f"{ratchetdir}/{hexhash}"
if os.path.isfile(ratchet_path): if os.path.isfile(ratchet_path):
@ -417,19 +417,19 @@ class Identity:
if packet.rssi != None or packet.snr != None: if packet.rssi != None or packet.snr != None:
signal_str = " [" signal_str = " ["
if packet.rssi != None: if packet.rssi != None:
signal_str += "RSSI "+str(packet.rssi)+"dBm" signal_str += f"RSSI {packet.rssi}dBm"
if packet.snr != None: if packet.snr != None:
signal_str += ", " signal_str += ", "
if packet.snr != None: if packet.snr != None:
signal_str += "SNR "+str(packet.snr)+"dB" signal_str += f"SNR {packet.snr}dB"
signal_str += "]" signal_str += "]"
else: else:
signal_str = "" signal_str = ""
if hasattr(packet, "transport_id") and packet.transport_id != None: if hasattr(packet, "transport_id") and packet.transport_id != None:
RNS.log("Valid announce for "+RNS.prettyhexrep(destination_hash)+" "+str(packet.hops)+" hops away, received via "+RNS.prettyhexrep(packet.transport_id)+" on "+str(packet.receiving_interface)+signal_str, RNS.LOG_EXTREME) RNS.log(f"Valid announce for {RNS.prettyhexrep(destination_hash)} {packet.hops} hops away, received via {RNS.prettyhexrep(packet.transport_id)} on {packet.receiving_interface}{signal_str}", RNS.LOG_EXTREME)
else: else:
RNS.log("Valid announce for "+RNS.prettyhexrep(destination_hash)+" "+str(packet.hops)+" hops away, received on "+str(packet.receiving_interface)+signal_str, RNS.LOG_EXTREME) RNS.log(f"Valid announce for {RNS.prettyhexrep(destination_hash)} {packet.hops} hops away, received on {packet.receiving_interface}{signal_str}", RNS.LOG_EXTREME)
if ratchet: if ratchet:
Identity._remember_ratchet(destination_hash, ratchet) Identity._remember_ratchet(destination_hash, ratchet)
@ -437,16 +437,16 @@ class Identity:
return True return True
else: else:
RNS.log("Received invalid announce for "+RNS.prettyhexrep(destination_hash)+": Destination mismatch.", RNS.LOG_DEBUG) RNS.log(f"Received invalid announce for {RNS.prettyhexrep(destination_hash)}: Destination mismatch.", RNS.LOG_DEBUG)
return False return False
else: else:
RNS.log("Received invalid announce for "+RNS.prettyhexrep(destination_hash)+": Invalid signature.", RNS.LOG_DEBUG) RNS.log(f"Received invalid announce for {RNS.prettyhexrep(destination_hash)}: Invalid signature.", RNS.LOG_DEBUG)
del announced_identity del announced_identity
return False return False
except Exception as e: except Exception as e:
RNS.log("Error occurred while validating announce. The contained exception was: "+str(e), RNS.LOG_ERROR) RNS.log(f"Error occurred while validating announce. The contained exception was: {e}", RNS.LOG_ERROR)
return False return False
@staticmethod @staticmethod
@ -505,8 +505,8 @@ class Identity:
return True return True
return False return False
except Exception as e: except Exception as e:
RNS.log("Error while saving identity to "+str(path), RNS.LOG_ERROR) RNS.log(f"Error while saving identity to {path}", RNS.LOG_ERROR)
RNS.log("The contained exception was: "+str(e)) RNS.log(f"The contained exception was: {e}")
def __init__(self,create_keys=True): def __init__(self,create_keys=True):
# Initialize keys to none # Initialize keys to none
@ -541,7 +541,7 @@ class Identity:
self.update_hashes() self.update_hashes()
RNS.log("Identity keys created for "+RNS.prettyhexrep(self.hash), RNS.LOG_VERBOSE) RNS.log(f"Identity keys created for {RNS.prettyhexrep(self.hash)}", RNS.LOG_VERBOSE)
def get_private_key(self): def get_private_key(self):
""" """
@ -581,7 +581,7 @@ class Identity:
except Exception as e: except Exception as e:
raise e raise e
RNS.log("Failed to load identity key", RNS.LOG_ERROR) RNS.log("Failed to load identity key", RNS.LOG_ERROR)
RNS.log("The contained exception was: "+str(e), RNS.LOG_ERROR) RNS.log(f"The contained exception was: {e}", RNS.LOG_ERROR)
return False return False
def load_public_key(self, pub_bytes): def load_public_key(self, pub_bytes):
@ -600,7 +600,7 @@ class Identity:
self.update_hashes() self.update_hashes()
except Exception as e: except Exception as e:
RNS.log("Error while loading public key, the contained exception was: "+str(e), RNS.LOG_ERROR) RNS.log(f"Error while loading public key, the contained exception was: {e}", RNS.LOG_ERROR)
def update_hashes(self): def update_hashes(self):
self.hash = Identity.truncated_hash(self.get_public_key()) self.hash = Identity.truncated_hash(self.get_public_key())
@ -613,8 +613,8 @@ class Identity:
return self.load_private_key(prv_bytes) return self.load_private_key(prv_bytes)
return False return False
except Exception as e: except Exception as e:
RNS.log("Error while loading identity from "+str(path), RNS.LOG_ERROR) RNS.log(f"Error while loading identity from {path}", RNS.LOG_ERROR)
RNS.log("The contained exception was: "+str(e), RNS.LOG_ERROR) RNS.log(f"The contained exception was: {e}", RNS.LOG_ERROR)
def get_salt(self): def get_salt(self):
return self.hash return self.hash
@ -697,7 +697,7 @@ class Identity:
pass pass
if enforce_ratchets and plaintext == None: if enforce_ratchets and plaintext == None:
RNS.log("Decryption with ratchet enforcement by "+RNS.prettyhexrep(self.hash)+" failed. Dropping packet.", RNS.LOG_DEBUG) RNS.log(f"Decryption with ratchet enforcement by {RNS.prettyhexrep(self.hash)} failed. Dropping packet.", RNS.LOG_DEBUG)
if ratchet_id_receiver: if ratchet_id_receiver:
ratchet_id_receiver.latest_ratchet_id = None ratchet_id_receiver.latest_ratchet_id = None
return None return None
@ -717,7 +717,7 @@ class Identity:
ratchet_id_receiver.latest_ratchet_id = None ratchet_id_receiver.latest_ratchet_id = None
except Exception as e: except Exception as e:
RNS.log("Decryption by "+RNS.prettyhexrep(self.hash)+" failed: "+str(e), RNS.LOG_DEBUG) RNS.log(f"Decryption by {RNS.prettyhexrep(self.hash)} failed: {e}", RNS.LOG_DEBUG)
if ratchet_id_receiver: if ratchet_id_receiver:
ratchet_id_receiver.latest_ratchet_id = None ratchet_id_receiver.latest_ratchet_id = None
@ -741,7 +741,7 @@ class Identity:
try: try:
return self.sig_prv.sign(message) return self.sig_prv.sign(message)
except Exception as e: except Exception as e:
RNS.log("The identity "+str(self)+" could not sign the requested message. The contained exception was: "+str(e), RNS.LOG_ERROR) RNS.log(f"The identity {self} could not sign the requested message. The contained exception was: {e}", RNS.LOG_ERROR)
raise e raise e
else: else:
raise KeyError("Signing failed because identity does not hold a private key") raise KeyError("Signing failed because identity does not hold a private key")

View File

@ -87,7 +87,7 @@ class AX25KISSInterface(Interface):
self.name = name self.name = name
self.src_call = callsign.upper().encode("ascii") self.src_call = callsign.upper().encode("ascii")
self.src_ssid = ssid self.src_ssid = ssid
self.dst_call = "APZRNS".encode("ascii") self.dst_call = b"APZRNS"
self.dst_ssid = 0 self.dst_ssid = 0
self.port = port self.port = port
self.speed = speed self.speed = speed
@ -105,10 +105,10 @@ class AX25KISSInterface(Interface):
self.flow_control_locked = time.time() self.flow_control_locked = time.time()
if (len(self.src_call) < 3 or len(self.src_call) > 6): if (len(self.src_call) < 3 or len(self.src_call) > 6):
raise ValueError("Invalid callsign for "+str(self)) raise ValueError(f"Invalid callsign for {self}")
if (self.src_ssid < 0 or self.src_ssid > 15): if (self.src_ssid < 0 or self.src_ssid > 15):
raise ValueError("Invalid SSID for "+str(self)) raise ValueError(f"Invalid SSID for {self}")
self.preamble = preamble if preamble != None else 350; self.preamble = preamble if preamble != None else 350;
self.txtail = txtail if txtail != None else 20; self.txtail = txtail if txtail != None else 20;
@ -124,16 +124,16 @@ class AX25KISSInterface(Interface):
try: try:
self.open_port() self.open_port()
except Exception as e: except Exception as e:
RNS.log("Could not open serial port for interface "+str(self), RNS.LOG_ERROR) RNS.log(f"Could not open serial port for interface {self}", RNS.LOG_ERROR)
raise e raise e
if self.serial.is_open: if self.serial.is_open:
self.configure_device() self.configure_device()
else: else:
raise IOError("Could not open serial port") raise OSError("Could not open serial port")
def open_port(self): def open_port(self):
RNS.log("Opening serial port "+self.port+"...", RNS.LOG_VERBOSE) RNS.log(f"Opening serial port {self.port}...", RNS.LOG_VERBOSE)
self.serial = self.pyserial.Serial( self.serial = self.pyserial.Serial(
port = self.port, port = self.port,
baudrate = self.speed, baudrate = self.speed,
@ -155,7 +155,7 @@ class AX25KISSInterface(Interface):
thread.daemon = True thread.daemon = True
thread.start() thread.start()
self.online = True self.online = True
RNS.log("Serial port "+self.port+" is now open") RNS.log(f"Serial port {self.port} is now open")
RNS.log("Configuring AX.25 KISS interface parameters...") RNS.log("Configuring AX.25 KISS interface parameters...")
self.setPreamble(self.preamble) self.setPreamble(self.preamble)
self.setTxTail(self.txtail) self.setTxTail(self.txtail)
@ -176,7 +176,7 @@ class AX25KISSInterface(Interface):
kiss_command = bytes([KISS.FEND])+bytes([KISS.CMD_TXDELAY])+bytes([preamble])+bytes([KISS.FEND]) kiss_command = bytes([KISS.FEND])+bytes([KISS.CMD_TXDELAY])+bytes([preamble])+bytes([KISS.FEND])
written = self.serial.write(kiss_command) written = self.serial.write(kiss_command)
if written != len(kiss_command): if written != len(kiss_command):
raise IOError("Could not configure AX.25 KISS interface preamble to "+str(preamble_ms)+" (command value "+str(preamble)+")") raise OSError(f"Could not configure AX.25 KISS interface preamble to {preamble_ms} (command value {preamble})")
def setTxTail(self, txtail): def setTxTail(self, txtail):
txtail_ms = txtail txtail_ms = txtail
@ -189,7 +189,7 @@ class AX25KISSInterface(Interface):
kiss_command = bytes([KISS.FEND])+bytes([KISS.CMD_TXTAIL])+bytes([txtail])+bytes([KISS.FEND]) kiss_command = bytes([KISS.FEND])+bytes([KISS.CMD_TXTAIL])+bytes([txtail])+bytes([KISS.FEND])
written = self.serial.write(kiss_command) written = self.serial.write(kiss_command)
if written != len(kiss_command): if written != len(kiss_command):
raise IOError("Could not configure AX.25 KISS interface TX tail to "+str(txtail_ms)+" (command value "+str(txtail)+")") raise OSError(f"Could not configure AX.25 KISS interface TX tail to {txtail_ms} (command value {txtail})")
def setPersistence(self, persistence): def setPersistence(self, persistence):
if persistence < 0: if persistence < 0:
@ -200,7 +200,7 @@ class AX25KISSInterface(Interface):
kiss_command = bytes([KISS.FEND])+bytes([KISS.CMD_P])+bytes([persistence])+bytes([KISS.FEND]) kiss_command = bytes([KISS.FEND])+bytes([KISS.CMD_P])+bytes([persistence])+bytes([KISS.FEND])
written = self.serial.write(kiss_command) written = self.serial.write(kiss_command)
if written != len(kiss_command): if written != len(kiss_command):
raise IOError("Could not configure AX.25 KISS interface persistence to "+str(persistence)) raise OSError(f"Could not configure AX.25 KISS interface persistence to {persistence}")
def setSlotTime(self, slottime): def setSlotTime(self, slottime):
slottime_ms = slottime slottime_ms = slottime
@ -213,16 +213,16 @@ class AX25KISSInterface(Interface):
kiss_command = bytes([KISS.FEND])+bytes([KISS.CMD_SLOTTIME])+bytes([slottime])+bytes([KISS.FEND]) kiss_command = bytes([KISS.FEND])+bytes([KISS.CMD_SLOTTIME])+bytes([slottime])+bytes([KISS.FEND])
written = self.serial.write(kiss_command) written = self.serial.write(kiss_command)
if written != len(kiss_command): if written != len(kiss_command):
raise IOError("Could not configure AX.25 KISS interface slot time to "+str(slottime_ms)+" (command value "+str(slottime)+")") raise OSError(f"Could not configure AX.25 KISS interface slot time to {slottime_ms} (command value {slottime})")
def setFlowControl(self, flow_control): def setFlowControl(self, flow_control):
kiss_command = bytes([KISS.FEND])+bytes([KISS.CMD_READY])+bytes([0x01])+bytes([KISS.FEND]) kiss_command = bytes([KISS.FEND])+bytes([KISS.CMD_READY])+bytes([0x01])+bytes([KISS.FEND])
written = self.serial.write(kiss_command) written = self.serial.write(kiss_command)
if written != len(kiss_command): if written != len(kiss_command):
if (flow_control): if (flow_control):
raise IOError("Could not enable AX.25 KISS interface flow control") raise OSError("Could not enable AX.25 KISS interface flow control")
else: else:
raise IOError("Could not enable AX.25 KISS interface flow control") raise OSError("Could not enable AX.25 KISS interface flow control")
def processIncoming(self, data): def processIncoming(self, data):
@ -270,7 +270,7 @@ class AX25KISSInterface(Interface):
if written != len(kiss_frame): if written != len(kiss_frame):
if self.flow_control: if self.flow_control:
self.interface_ready = True self.interface_ready = True
raise IOError("AX.25 interface only wrote "+str(written)+" bytes of "+str(len(kiss_frame))) raise OSError(f"AX.25 interface only wrote {written} bytes of {len(kiss_frame)}")
else: else:
self.queue(data) self.queue(data)
@ -336,13 +336,13 @@ class AX25KISSInterface(Interface):
if self.flow_control: if self.flow_control:
if not self.interface_ready: if not self.interface_ready:
if time.time() > self.flow_control_locked + self.flow_control_timeout: if time.time() > self.flow_control_locked + self.flow_control_timeout:
RNS.log("Interface "+str(self)+" is unlocking flow control due to time-out. This should not happen. Your hardware might have missed a flow-control READY command, or maybe it does not support flow-control.", RNS.LOG_WARNING) RNS.log(f"Interface {self} is unlocking flow control due to time-out. This should not happen. Your hardware might have missed a flow-control READY command, or maybe it does not support flow-control.", RNS.LOG_WARNING)
self.process_queue() self.process_queue()
except Exception as e: except Exception as e:
self.online = False self.online = False
RNS.log("A serial port error occurred, the contained exception was: "+str(e), RNS.LOG_ERROR) RNS.log(f"A serial port error occurred, the contained exception was: {e}", RNS.LOG_ERROR)
RNS.log("The interface "+str(self)+" experienced an unrecoverable error and is now offline.", RNS.LOG_ERROR) RNS.log(f"The interface {self} experienced an unrecoverable error and is now offline.", RNS.LOG_ERROR)
if RNS.Reticulum.panic_on_interface_error: if RNS.Reticulum.panic_on_interface_error:
RNS.panic() RNS.panic()
@ -357,17 +357,17 @@ class AX25KISSInterface(Interface):
while not self.online: while not self.online:
try: try:
time.sleep(5) time.sleep(5)
RNS.log("Attempting to reconnect serial port "+str(self.port)+" for "+str(self)+"...", RNS.LOG_VERBOSE) RNS.log(f"Attempting to reconnect serial port {self.port} for {self}...", RNS.LOG_VERBOSE)
self.open_port() self.open_port()
if self.serial.is_open: if self.serial.is_open:
self.configure_device() self.configure_device()
except Exception as e: except Exception as e:
RNS.log("Error while reconnecting port, the contained exception was: "+str(e), RNS.LOG_ERROR) RNS.log(f"Error while reconnecting port, the contained exception was: {e}", RNS.LOG_ERROR)
RNS.log("Reconnected serial port for "+str(self)) RNS.log(f"Reconnected serial port for {self}")
def should_ingress_limit(self): def should_ingress_limit(self):
return False return False
def __str__(self): def __str__(self):
return "AX25KISSInterface["+self.name+"]" return f"AX25KISSInterface[{self.name}]"

View File

@ -125,17 +125,17 @@ class KISSInterface(Interface):
try: try:
self.open_port() self.open_port()
except Exception as e: except Exception as e:
RNS.log("Could not open serial port "+self.port, RNS.LOG_ERROR) RNS.log(f"Could not open serial port {self.port}", RNS.LOG_ERROR)
raise e raise e
if self.serial.is_open: if self.serial.is_open:
self.configure_device() self.configure_device()
else: else:
raise IOError("Could not open serial port") raise OSError("Could not open serial port")
def open_port(self): def open_port(self):
RNS.log("Opening serial port "+self.port+"...") RNS.log(f"Opening serial port {self.port}...")
# Get device parameters # Get device parameters
from usb4a import usb from usb4a import usb
device = usb.get_usb_device(self.port) device = usb.get_usb_device(self.port)
@ -147,7 +147,7 @@ class KISSInterface(Interface):
proxy = self.pyserial.get_serial_port proxy = self.pyserial.get_serial_port
if vid == 0x1A86 and pid == 0x55D4: if vid == 0x1A86 and pid == 0x55D4:
# Force CDC driver for Qinheng CH34x # Force CDC driver for Qinheng CH34x
RNS.log(str(self)+" using CDC driver for "+RNS.hexrep(vid)+":"+RNS.hexrep(pid), RNS.LOG_DEBUG) RNS.log(f"{self} using CDC driver for {RNS.hexrep(vid)}:{RNS.hexrep(pid)}", RNS.LOG_DEBUG)
from usbserial4a.cdcacmserial4a import CdcAcmSerial from usbserial4a.cdcacmserial4a import CdcAcmSerial
proxy = CdcAcmSerial proxy = CdcAcmSerial
@ -186,9 +186,9 @@ class KISSInterface(Interface):
self.serial.USB_READ_TIMEOUT_MILLIS = 100 self.serial.USB_READ_TIMEOUT_MILLIS = 100
self.serial.timeout = 0.1 self.serial.timeout = 0.1
RNS.log(str(self)+" USB read buffer size set to "+RNS.prettysize(self.serial.DEFAULT_READ_BUFFER_SIZE), RNS.LOG_DEBUG) RNS.log(f"{self} USB read buffer size set to {RNS.prettysize(self.serial.DEFAULT_READ_BUFFER_SIZE)}", RNS.LOG_DEBUG)
RNS.log(str(self)+" USB read timeout set to "+str(self.serial.USB_READ_TIMEOUT_MILLIS)+"ms", RNS.LOG_DEBUG) RNS.log(f"{self} USB read timeout set to {self.serial.USB_READ_TIMEOUT_MILLIS}ms", RNS.LOG_DEBUG)
RNS.log(str(self)+" USB write timeout set to "+str(self.serial.USB_WRITE_TIMEOUT_MILLIS)+"ms", RNS.LOG_DEBUG) RNS.log(f"{self} USB write timeout set to {self.serial.USB_WRITE_TIMEOUT_MILLIS}ms", RNS.LOG_DEBUG)
def configure_device(self): def configure_device(self):
# Allow time for interface to initialise before config # Allow time for interface to initialise before config
@ -197,7 +197,7 @@ class KISSInterface(Interface):
thread.daemon = True thread.daemon = True
thread.start() thread.start()
self.online = True self.online = True
RNS.log("Serial port "+self.port+" is now open") RNS.log(f"Serial port {self.port} is now open")
RNS.log("Configuring KISS interface parameters...") RNS.log("Configuring KISS interface parameters...")
self.setPreamble(self.preamble) self.setPreamble(self.preamble)
self.setTxTail(self.txtail) self.setTxTail(self.txtail)
@ -218,7 +218,7 @@ class KISSInterface(Interface):
kiss_command = bytes([KISS.FEND])+bytes([KISS.CMD_TXDELAY])+bytes([preamble])+bytes([KISS.FEND]) kiss_command = bytes([KISS.FEND])+bytes([KISS.CMD_TXDELAY])+bytes([preamble])+bytes([KISS.FEND])
written = self.serial.write(kiss_command) written = self.serial.write(kiss_command)
if written != len(kiss_command): if written != len(kiss_command):
raise IOError("Could not configure KISS interface preamble to "+str(preamble_ms)+" (command value "+str(preamble)+")") raise OSError(f"Could not configure KISS interface preamble to {preamble_ms} (command value {preamble})")
def setTxTail(self, txtail): def setTxTail(self, txtail):
txtail_ms = txtail txtail_ms = txtail
@ -231,7 +231,7 @@ class KISSInterface(Interface):
kiss_command = bytes([KISS.FEND])+bytes([KISS.CMD_TXTAIL])+bytes([txtail])+bytes([KISS.FEND]) kiss_command = bytes([KISS.FEND])+bytes([KISS.CMD_TXTAIL])+bytes([txtail])+bytes([KISS.FEND])
written = self.serial.write(kiss_command) written = self.serial.write(kiss_command)
if written != len(kiss_command): if written != len(kiss_command):
raise IOError("Could not configure KISS interface TX tail to "+str(txtail_ms)+" (command value "+str(txtail)+")") raise OSError(f"Could not configure KISS interface TX tail to {txtail_ms} (command value {txtail})")
def setPersistence(self, persistence): def setPersistence(self, persistence):
if persistence < 0: if persistence < 0:
@ -242,7 +242,7 @@ class KISSInterface(Interface):
kiss_command = bytes([KISS.FEND])+bytes([KISS.CMD_P])+bytes([persistence])+bytes([KISS.FEND]) kiss_command = bytes([KISS.FEND])+bytes([KISS.CMD_P])+bytes([persistence])+bytes([KISS.FEND])
written = self.serial.write(kiss_command) written = self.serial.write(kiss_command)
if written != len(kiss_command): if written != len(kiss_command):
raise IOError("Could not configure KISS interface persistence to "+str(persistence)) raise OSError(f"Could not configure KISS interface persistence to {persistence}")
def setSlotTime(self, slottime): def setSlotTime(self, slottime):
slottime_ms = slottime slottime_ms = slottime
@ -255,16 +255,16 @@ class KISSInterface(Interface):
kiss_command = bytes([KISS.FEND])+bytes([KISS.CMD_SLOTTIME])+bytes([slottime])+bytes([KISS.FEND]) kiss_command = bytes([KISS.FEND])+bytes([KISS.CMD_SLOTTIME])+bytes([slottime])+bytes([KISS.FEND])
written = self.serial.write(kiss_command) written = self.serial.write(kiss_command)
if written != len(kiss_command): if written != len(kiss_command):
raise IOError("Could not configure KISS interface slot time to "+str(slottime_ms)+" (command value "+str(slottime)+")") raise OSError(f"Could not configure KISS interface slot time to {slottime_ms} (command value {slottime})")
def setFlowControl(self, flow_control): def setFlowControl(self, flow_control):
kiss_command = bytes([KISS.FEND])+bytes([KISS.CMD_READY])+bytes([0x01])+bytes([KISS.FEND]) kiss_command = bytes([KISS.FEND])+bytes([KISS.CMD_READY])+bytes([0x01])+bytes([KISS.FEND])
written = self.serial.write(kiss_command) written = self.serial.write(kiss_command)
if written != len(kiss_command): if written != len(kiss_command):
if (flow_control): if (flow_control):
raise IOError("Could not enable KISS interface flow control") raise OSError("Could not enable KISS interface flow control")
else: else:
raise IOError("Could not enable KISS interface flow control") raise OSError("Could not enable KISS interface flow control")
def processIncoming(self, data): def processIncoming(self, data):
@ -295,7 +295,7 @@ class KISSInterface(Interface):
self.first_tx = time.time() self.first_tx = time.time()
if written != len(frame): if written != len(frame):
raise IOError("Serial interface only wrote "+str(written)+" bytes of "+str(len(data))) raise OSError(f"Serial interface only wrote {written} bytes of {len(data)}")
else: else:
self.queue(data) self.queue(data)
@ -365,20 +365,20 @@ class KISSInterface(Interface):
if self.flow_control: if self.flow_control:
if not self.interface_ready: if not self.interface_ready:
if time.time() > self.flow_control_locked + self.flow_control_timeout: if time.time() > self.flow_control_locked + self.flow_control_timeout:
RNS.log("Interface "+str(self)+" is unlocking flow control due to time-out. This should not happen. Your hardware might have missed a flow-control READY command, or maybe it does not support flow-control.", RNS.LOG_WARNING) RNS.log(f"Interface {self} is unlocking flow control due to time-out. This should not happen. Your hardware might have missed a flow-control READY command, or maybe it does not support flow-control.", RNS.LOG_WARNING)
self.process_queue() self.process_queue()
if self.beacon_i != None and self.beacon_d != None: if self.beacon_i != None and self.beacon_d != None:
if self.first_tx != None: if self.first_tx != None:
if time.time() > self.first_tx + self.beacon_i: if time.time() > self.first_tx + self.beacon_i:
RNS.log("Interface "+str(self)+" is transmitting beacon data: "+str(self.beacon_d.decode("utf-8")), RNS.LOG_DEBUG) RNS.log(f"Interface {self} is transmitting beacon data: {self.beacon_d.decode('utf-8')}", RNS.LOG_DEBUG)
self.first_tx = None self.first_tx = None
self.processOutgoing(self.beacon_d) self.processOutgoing(self.beacon_d)
except Exception as e: except Exception as e:
self.online = False self.online = False
RNS.log("A serial port error occurred, the contained exception was: "+str(e), RNS.LOG_ERROR) RNS.log(f"A serial port error occurred, the contained exception was: {e}", RNS.LOG_ERROR)
RNS.log("The interface "+str(self)+" experienced an unrecoverable error and is now offline.", RNS.LOG_ERROR) RNS.log(f"The interface {self} experienced an unrecoverable error and is now offline.", RNS.LOG_ERROR)
if RNS.Reticulum.panic_on_interface_error: if RNS.Reticulum.panic_on_interface_error:
RNS.panic() RNS.panic()
@ -393,17 +393,17 @@ class KISSInterface(Interface):
while not self.online: while not self.online:
try: try:
time.sleep(5) time.sleep(5)
RNS.log("Attempting to reconnect serial port "+str(self.port)+" for "+str(self)+"...", RNS.LOG_VERBOSE) RNS.log(f"Attempting to reconnect serial port {self.port} for {self}...", RNS.LOG_VERBOSE)
self.open_port() self.open_port()
if self.serial.is_open: if self.serial.is_open:
self.configure_device() self.configure_device()
except Exception as e: except Exception as e:
RNS.log("Error while reconnecting port, the contained exception was: "+str(e), RNS.LOG_ERROR) RNS.log(f"Error while reconnecting port, the contained exception was: {e}", RNS.LOG_ERROR)
RNS.log("Reconnected serial port for "+str(self)) RNS.log(f"Reconnected serial port for {self}")
def should_ingress_limit(self): def should_ingress_limit(self):
return False return False
def __str__(self): def __str__(self):
return "KISSInterface["+self.name+"]" return f"KISSInterface[{self.name}]"

View File

@ -172,7 +172,7 @@ class AndroidBluetoothManager():
try: try:
self.rfcomm_socket = device.createRfcommSocketToServiceRecord(self.bt_rfcomm_service_record) self.rfcomm_socket = device.createRfcommSocketToServiceRecord(self.bt_rfcomm_service_record)
if self.rfcomm_socket == None: if self.rfcomm_socket == None:
raise IOError("Bluetooth stack returned no socket object") raise OSError("Bluetooth stack returned no socket object")
else: else:
if not self.rfcomm_socket.isConnected(): if not self.rfcomm_socket.isConnected():
try: try:
@ -181,13 +181,13 @@ class AndroidBluetoothManager():
self.rfcomm_writer = self.rfcomm_socket.getOutputStream() self.rfcomm_writer = self.rfcomm_socket.getOutputStream()
self.connected = True self.connected = True
self.connected_device = device self.connected_device = device
RNS.log("Bluetooth device "+str(self.connected_device.getName())+" "+str(self.connected_device.getAddress())+" connected.") RNS.log(f"Bluetooth device {self.connected_device.getName()} {self.connected_device.getAddress()} connected.")
except Exception as e: except Exception as e:
raise IOError("The Bluetooth RFcomm socket could not be connected: "+str(e)) raise OSError(f"The Bluetooth RFcomm socket could not be connected: {e}")
except Exception as e: except Exception as e:
RNS.log("Could not create and connect Bluetooth RFcomm socket for "+str(device.getName())+" "+str(device.getAddress()), RNS.LOG_EXTREME) RNS.log(f"Could not create and connect Bluetooth RFcomm socket for {device.getName()} {device.getAddress()}", RNS.LOG_EXTREME)
RNS.log("The contained exception was: "+str(e), RNS.LOG_EXTREME) RNS.log(f"The contained exception was: {e}", RNS.LOG_EXTREME)
def close(self): def close(self):
if self.connected: if self.connected:
@ -209,7 +209,7 @@ class AndroidBluetoothManager():
def read(self, len = None): def read(self, len = None):
if self.connection_failed: if self.connection_failed:
raise IOError("Bluetooth connection failed") raise OSError("Bluetooth connection failed")
else: else:
if self.connected and self.rfcomm_reader != None: if self.connected and self.rfcomm_reader != None:
available = self.rfcomm_reader.available() available = self.rfcomm_reader.available()
@ -223,7 +223,7 @@ class AndroidBluetoothManager():
else: else:
return bytes([]) return bytes([])
else: else:
raise IOError("No RFcomm socket available") raise OSError("No RFcomm socket available")
def write(self, data): def write(self, data):
try: try:
@ -231,7 +231,7 @@ class AndroidBluetoothManager():
self.rfcomm_writer.flush() self.rfcomm_writer.flush()
return len(data) return len(data)
except Exception as e: except Exception as e:
RNS.log("Bluetooth connection failed for "+str(self), RNS.LOG_ERROR) RNS.log(f"Bluetooth connection failed for {self}", RNS.LOG_ERROR)
self.connection_failed = True self.connection_failed = True
return 0 return 0
@ -274,7 +274,7 @@ class RNodeInterface(Interface):
bluetooth_state = 0x00 bluetooth_state = 0x00
if port != None: if port != None:
RNS.log("Opening serial port "+port+"...") RNS.log(f"Opening serial port {port}...")
# Get device parameters # Get device parameters
from usb4a import usb from usb4a import usb
device = usb.get_usb_device(port) device = usb.get_usb_device(port)
@ -287,7 +287,7 @@ class RNodeInterface(Interface):
proxy = pyserial.get_serial_port proxy = pyserial.get_serial_port
if vid == 0x1A86 and pid == 0x55D4: if vid == 0x1A86 and pid == 0x55D4:
# Force CDC driver for Qinheng CH34x # Force CDC driver for Qinheng CH34x
RNS.log("Using CDC driver for "+RNS.hexrep(vid)+":"+RNS.hexrep(pid), RNS.LOG_DEBUG) RNS.log(f"Using CDC driver for {RNS.hexrep(vid)}:{RNS.hexrep(pid)}", RNS.LOG_DEBUG)
from usbserial4a.cdcacmserial4a import CdcAcmSerial from usbserial4a.cdcacmserial4a import CdcAcmSerial
proxy = CdcAcmSerial proxy = CdcAcmSerial
@ -466,31 +466,31 @@ class RNodeInterface(Interface):
self.validcfg = True self.validcfg = True
if (self.frequency < RNodeInterface.FREQ_MIN or self.frequency > RNodeInterface.FREQ_MAX): if (self.frequency < RNodeInterface.FREQ_MIN or self.frequency > RNodeInterface.FREQ_MAX):
RNS.log("Invalid frequency configured for "+str(self), RNS.LOG_ERROR) RNS.log(f"Invalid frequency configured for {self}", RNS.LOG_ERROR)
self.validcfg = False self.validcfg = False
if (self.txpower < 0 or self.txpower > 22): if (self.txpower < 0 or self.txpower > 22):
RNS.log("Invalid TX power configured for "+str(self), RNS.LOG_ERROR) RNS.log(f"Invalid TX power configured for {self}", RNS.LOG_ERROR)
self.validcfg = False self.validcfg = False
if (self.bandwidth < 7800 or self.bandwidth > 500000): if (self.bandwidth < 7800 or self.bandwidth > 500000):
RNS.log("Invalid bandwidth configured for "+str(self), RNS.LOG_ERROR) RNS.log(f"Invalid bandwidth configured for {self}", RNS.LOG_ERROR)
self.validcfg = False self.validcfg = False
if (self.sf < 5 or self.sf > 12): if (self.sf < 5 or self.sf > 12):
RNS.log("Invalid spreading factor configured for "+str(self), RNS.LOG_ERROR) RNS.log(f"Invalid spreading factor configured for {self}", RNS.LOG_ERROR)
self.validcfg = False self.validcfg = False
if (self.cr < 5 or self.cr > 8): if (self.cr < 5 or self.cr > 8):
RNS.log("Invalid coding rate configured for "+str(self), RNS.LOG_ERROR) RNS.log(f"Invalid coding rate configured for {self}", RNS.LOG_ERROR)
self.validcfg = False self.validcfg = False
if (self.st_alock and (self.st_alock < 0.0 or self.st_alock > 100.0)): if (self.st_alock and (self.st_alock < 0.0 or self.st_alock > 100.0)):
RNS.log("Invalid short-term airtime limit configured for "+str(self), RNS.LOG_ERROR) RNS.log(f"Invalid short-term airtime limit configured for {self}", RNS.LOG_ERROR)
self.validcfg = False self.validcfg = False
if (self.lt_alock and (self.lt_alock < 0.0 or self.lt_alock > 100.0)): if (self.lt_alock and (self.lt_alock < 0.0 or self.lt_alock > 100.0)):
RNS.log("Invalid long-term airtime limit configured for "+str(self), RNS.LOG_ERROR) RNS.log(f"Invalid long-term airtime limit configured for {self}", RNS.LOG_ERROR)
self.validcfg = False self.validcfg = False
if id_interval != None and id_callsign != None: if id_interval != None and id_callsign != None:
@ -499,14 +499,14 @@ class RNodeInterface(Interface):
self.id_callsign = id_callsign.encode("utf-8") self.id_callsign = id_callsign.encode("utf-8")
self.id_interval = id_interval self.id_interval = id_interval
else: else:
RNS.log("The encoded ID callsign for "+str(self)+" exceeds the max length of "+str(RNodeInterface.CALLSIGN_MAX_LEN)+" bytes.", RNS.LOG_ERROR) RNS.log(f"The encoded ID callsign for {self} exceeds the max length of {RNodeInterface.CALLSIGN_MAX_LEN} bytes.", RNS.LOG_ERROR)
self.validcfg = False self.validcfg = False
else: else:
self.id_interval = None self.id_interval = None
self.id_callsign = None self.id_callsign = None
if (not self.validcfg): if (not self.validcfg):
raise ValueError("The configuration for "+str(self)+" contains errors, interface is offline") raise ValueError(f"The configuration for {self} contains errors, interface is offline")
try: try:
self.open_port() self.open_port()
@ -515,18 +515,18 @@ class RNodeInterface(Interface):
if self.serial.is_open: if self.serial.is_open:
self.configure_device() self.configure_device()
else: else:
raise IOError("Could not open serial port") raise OSError("Could not open serial port")
elif self.bt_manager != None: elif self.bt_manager != None:
if self.bt_manager.connected: if self.bt_manager.connected:
self.configure_device() self.configure_device()
else: else:
raise IOError("Could not connect to any Bluetooth devices") raise OSError("Could not connect to any Bluetooth devices")
else: else:
raise IOError("Neither serial port nor Bluetooth devices available") raise OSError("Neither serial port nor Bluetooth devices available")
except Exception as e: except Exception as e:
RNS.log("Could not open serial port for interface "+str(self), RNS.LOG_ERROR) RNS.log(f"Could not open serial port for interface {self}", RNS.LOG_ERROR)
RNS.log("The contained exception was: "+str(e), RNS.LOG_ERROR) RNS.log(f"The contained exception was: {e}", RNS.LOG_ERROR)
if len(self.hw_errors) == 0: if len(self.hw_errors) == 0:
RNS.log("Reticulum will attempt to bring up this interface periodically", RNS.LOG_ERROR) RNS.log("Reticulum will attempt to bring up this interface periodically", RNS.LOG_ERROR)
thread = threading.Thread(target=self.reconnect_port, daemon=True).start() thread = threading.Thread(target=self.reconnect_port, daemon=True).start()
@ -538,7 +538,7 @@ class RNodeInterface(Interface):
elif self.bt_manager != None: elif self.bt_manager != None:
return self.bt_manager.read() return self.bt_manager.read()
else: else:
raise IOError("No ports available for reading") raise OSError("No ports available for reading")
def write_mux(self, data): def write_mux(self, data):
if self.serial != None: if self.serial != None:
@ -551,7 +551,7 @@ class RNodeInterface(Interface):
self.last_port_io = time.time() self.last_port_io = time.time()
return written return written
else: else:
raise IOError("No ports available for writing") raise OSError("No ports available for writing")
# def reset_ble(self): # def reset_ble(self):
# RNS.log(f"Clearing previous connection instance: "+str(self.ble)) # RNS.log(f"Clearing previous connection instance: "+str(self.ble))
@ -565,7 +565,7 @@ class RNodeInterface(Interface):
def open_port(self): def open_port(self):
if not self.use_ble: if not self.use_ble:
if self.port != None: if self.port != None:
RNS.log("Opening serial port "+self.port+"...") RNS.log(f"Opening serial port {self.port}...")
# Get device parameters # Get device parameters
from usb4a import usb from usb4a import usb
device = usb.get_usb_device(self.port) device = usb.get_usb_device(self.port)
@ -577,7 +577,7 @@ class RNodeInterface(Interface):
proxy = self.pyserial.get_serial_port proxy = self.pyserial.get_serial_port
if vid == 0x1A86 and pid == 0x55D4: if vid == 0x1A86 and pid == 0x55D4:
# Force CDC driver for Qinheng CH34x # Force CDC driver for Qinheng CH34x
RNS.log(str(self)+" using CDC driver for "+RNS.hexrep(vid)+":"+RNS.hexrep(pid), RNS.LOG_DEBUG) RNS.log(f"{self} using CDC driver for {RNS.hexrep(vid)}:{RNS.hexrep(pid)}", RNS.LOG_DEBUG)
from usbserial4a.cdcacmserial4a import CdcAcmSerial from usbserial4a.cdcacmserial4a import CdcAcmSerial
proxy = CdcAcmSerial proxy = CdcAcmSerial
@ -616,9 +616,9 @@ class RNodeInterface(Interface):
self.serial.USB_READ_TIMEOUT_MILLIS = 100 self.serial.USB_READ_TIMEOUT_MILLIS = 100
self.serial.timeout = 0.1 self.serial.timeout = 0.1
RNS.log(str(self)+" USB read buffer size set to "+RNS.prettysize(self.serial.DEFAULT_READ_BUFFER_SIZE), RNS.LOG_DEBUG) RNS.log(f"{self} USB read buffer size set to {RNS.prettysize(self.serial.DEFAULT_READ_BUFFER_SIZE)}", RNS.LOG_DEBUG)
RNS.log(str(self)+" USB read timeout set to "+str(self.serial.USB_READ_TIMEOUT_MILLIS)+"ms", RNS.LOG_DEBUG) RNS.log(f"{self} USB read timeout set to {self.serial.USB_READ_TIMEOUT_MILLIS}ms", RNS.LOG_DEBUG)
RNS.log(str(self)+" USB write timeout set to "+str(self.serial.USB_WRITE_TIMEOUT_MILLIS)+"ms", RNS.LOG_DEBUG) RNS.log(f"{self} USB write timeout set to {self.serial.USB_WRITE_TIMEOUT_MILLIS}ms", RNS.LOG_DEBUG)
elif self.allow_bluetooth: elif self.allow_bluetooth:
if self.bt_manager == None: if self.bt_manager == None:
@ -660,17 +660,17 @@ class RNodeInterface(Interface):
RNS.log(f"RNode detect timed out over {self.port}", RNS.LOG_ERROR) RNS.log(f"RNode detect timed out over {self.port}", RNS.LOG_ERROR)
if not self.detected: if not self.detected:
raise IOError("Could not detect device") raise OSError("Could not detect device")
else: else:
if self.platform == KISS.PLATFORM_ESP32 or self.platform == KISS.PLATFORM_NRF52: if self.platform == KISS.PLATFORM_ESP32 or self.platform == KISS.PLATFORM_NRF52:
self.display = True self.display = True
if not self.firmware_ok: if not self.firmware_ok:
raise IOError("Invalid device firmware") raise OSError("Invalid device firmware")
if self.serial != None and self.port != None: if self.serial != None and self.port != None:
self.timeout = 200 self.timeout = 200
RNS.log("Serial port "+self.port+" is now open") RNS.log(f"Serial port {self.port} is now open")
if self.bt_manager != None and self.bt_manager.connected: if self.bt_manager != None and self.bt_manager.connected:
self.timeout = 1500 self.timeout = 1500
@ -680,11 +680,11 @@ class RNodeInterface(Interface):
self.initRadio() self.initRadio()
if (self.validateRadioState()): if (self.validateRadioState()):
self.interface_ready = True self.interface_ready = True
RNS.log(str(self)+" is configured and powered up") RNS.log(f"{self} is configured and powered up")
sleep(0.3) sleep(0.3)
self.online = True self.online = True
else: else:
RNS.log("After configuring "+str(self)+", the reported radio parameters did not match your configuration.", RNS.LOG_ERROR) RNS.log(f"After configuring {self}, the reported radio parameters did not match your configuration.", RNS.LOG_ERROR)
RNS.log("Make sure that your hardware actually supports the parameters specified in the configuration", RNS.LOG_ERROR) RNS.log("Make sure that your hardware actually supports the parameters specified in the configuration", RNS.LOG_ERROR)
RNS.log("Aborting RNode startup", RNS.LOG_ERROR) RNS.log("Aborting RNode startup", RNS.LOG_ERROR)
@ -693,7 +693,7 @@ class RNodeInterface(Interface):
if self.bt_manager != None: if self.bt_manager != None:
self.bt_manager.close() self.bt_manager.close()
raise IOError("RNode interface did not pass configuration validation") raise OSError("RNode interface did not pass configuration validation")
def initRadio(self): def initRadio(self):
@ -728,45 +728,45 @@ class RNodeInterface(Interface):
kiss_command = bytes([KISS.FEND, KISS.CMD_DETECT, KISS.DETECT_REQ, KISS.FEND, KISS.CMD_FW_VERSION, 0x00, KISS.FEND, KISS.CMD_PLATFORM, 0x00, KISS.FEND, KISS.CMD_MCU, 0x00, KISS.FEND]) kiss_command = bytes([KISS.FEND, KISS.CMD_DETECT, KISS.DETECT_REQ, KISS.FEND, KISS.CMD_FW_VERSION, 0x00, KISS.FEND, KISS.CMD_PLATFORM, 0x00, KISS.FEND, KISS.CMD_MCU, 0x00, KISS.FEND])
written = self.write_mux(kiss_command) written = self.write_mux(kiss_command)
if written != len(kiss_command): if written != len(kiss_command):
raise IOError("An IO error occurred while detecting hardware for "+str(self)) raise OSError(f"An IO error occurred while detecting hardware for {self}")
def leave(self): def leave(self):
kiss_command = bytes([KISS.FEND, KISS.CMD_LEAVE, 0xFF, KISS.FEND]) kiss_command = bytes([KISS.FEND, KISS.CMD_LEAVE, 0xFF, KISS.FEND])
written = self.write_mux(kiss_command) written = self.write_mux(kiss_command)
if written != len(kiss_command): if written != len(kiss_command):
raise IOError("An IO error occurred while sending host left command to device") raise OSError("An IO error occurred while sending host left command to device")
def enable_bluetooth(self): def enable_bluetooth(self):
kiss_command = bytes([KISS.FEND, KISS.CMD_BT_CTRL, 0x01, KISS.FEND]) kiss_command = bytes([KISS.FEND, KISS.CMD_BT_CTRL, 0x01, KISS.FEND])
written = self.write_mux(kiss_command) written = self.write_mux(kiss_command)
if written != len(kiss_command): if written != len(kiss_command):
raise IOError("An IO error occurred while sending bluetooth enable command to device") raise OSError("An IO error occurred while sending bluetooth enable command to device")
def disable_bluetooth(self): def disable_bluetooth(self):
kiss_command = bytes([KISS.FEND, KISS.CMD_BT_CTRL, 0x00, KISS.FEND]) kiss_command = bytes([KISS.FEND, KISS.CMD_BT_CTRL, 0x00, KISS.FEND])
written = self.write_mux(kiss_command) written = self.write_mux(kiss_command)
if written != len(kiss_command): if written != len(kiss_command):
raise IOError("An IO error occurred while sending bluetooth disable command to device") raise OSError("An IO error occurred while sending bluetooth disable command to device")
def bluetooth_pair(self): def bluetooth_pair(self):
kiss_command = bytes([KISS.FEND, KISS.CMD_BT_CTRL, 0x02, KISS.FEND]) kiss_command = bytes([KISS.FEND, KISS.CMD_BT_CTRL, 0x02, KISS.FEND])
written = self.write_mux(kiss_command) written = self.write_mux(kiss_command)
if written != len(kiss_command): if written != len(kiss_command):
raise IOError("An IO error occurred while sending bluetooth pair command to device") raise OSError("An IO error occurred while sending bluetooth pair command to device")
def enable_external_framebuffer(self): def enable_external_framebuffer(self):
if self.display != None: if self.display != None:
kiss_command = bytes([KISS.FEND, KISS.CMD_FB_EXT, 0x01, KISS.FEND]) kiss_command = bytes([KISS.FEND, KISS.CMD_FB_EXT, 0x01, KISS.FEND])
written = self.write_mux(kiss_command) written = self.write_mux(kiss_command)
if written != len(kiss_command): if written != len(kiss_command):
raise IOError("An IO error occurred while enabling external framebuffer on device") raise OSError("An IO error occurred while enabling external framebuffer on device")
def disable_external_framebuffer(self): def disable_external_framebuffer(self):
if self.display != None: if self.display != None:
kiss_command = bytes([KISS.FEND, KISS.CMD_FB_EXT, 0x00, KISS.FEND]) kiss_command = bytes([KISS.FEND, KISS.CMD_FB_EXT, 0x00, KISS.FEND])
written = self.write_mux(kiss_command) written = self.write_mux(kiss_command)
if written != len(kiss_command): if written != len(kiss_command):
raise IOError("An IO error occurred while disabling external framebuffer on device") raise OSError("An IO error occurred while disabling external framebuffer on device")
FB_PIXEL_WIDTH = 64 FB_PIXEL_WIDTH = 64
FB_BITS_PER_PIXEL = 1 FB_BITS_PER_PIXEL = 1
@ -791,13 +791,13 @@ class RNodeInterface(Interface):
written = self.write_mux(kiss_command) written = self.write_mux(kiss_command)
if written != len(kiss_command): if written != len(kiss_command):
raise IOError("An IO error occurred while writing framebuffer data device") raise OSError("An IO error occurred while writing framebuffer data device")
def hard_reset(self): def hard_reset(self):
kiss_command = bytes([KISS.FEND, KISS.CMD_RESET, 0xf8, KISS.FEND]) kiss_command = bytes([KISS.FEND, KISS.CMD_RESET, 0xf8, KISS.FEND])
written = self.write_mux(kiss_command) written = self.write_mux(kiss_command)
if written != len(kiss_command): if written != len(kiss_command):
raise IOError("An IO error occurred while restarting device") raise OSError("An IO error occurred while restarting device")
sleep(4.0); sleep(4.0);
def setFrequency(self): def setFrequency(self):
@ -810,7 +810,7 @@ class RNodeInterface(Interface):
kiss_command = bytes([KISS.FEND])+bytes([KISS.CMD_FREQUENCY])+data+bytes([KISS.FEND]) kiss_command = bytes([KISS.FEND])+bytes([KISS.CMD_FREQUENCY])+data+bytes([KISS.FEND])
written = self.write_mux(kiss_command) written = self.write_mux(kiss_command)
if written != len(kiss_command): if written != len(kiss_command):
raise IOError("An IO error occurred while configuring frequency for "+str(self)) raise OSError(f"An IO error occurred while configuring frequency for {self}")
def setBandwidth(self): def setBandwidth(self):
c1 = self.bandwidth >> 24 c1 = self.bandwidth >> 24
@ -822,28 +822,28 @@ class RNodeInterface(Interface):
kiss_command = bytes([KISS.FEND])+bytes([KISS.CMD_BANDWIDTH])+data+bytes([KISS.FEND]) kiss_command = bytes([KISS.FEND])+bytes([KISS.CMD_BANDWIDTH])+data+bytes([KISS.FEND])
written = self.write_mux(kiss_command) written = self.write_mux(kiss_command)
if written != len(kiss_command): if written != len(kiss_command):
raise IOError("An IO error occurred while configuring bandwidth for "+str(self)) raise OSError(f"An IO error occurred while configuring bandwidth for {self}")
def setTXPower(self): def setTXPower(self):
txp = bytes([self.txpower]) txp = bytes([self.txpower])
kiss_command = bytes([KISS.FEND])+bytes([KISS.CMD_TXPOWER])+txp+bytes([KISS.FEND]) kiss_command = bytes([KISS.FEND])+bytes([KISS.CMD_TXPOWER])+txp+bytes([KISS.FEND])
written = self.write_mux(kiss_command) written = self.write_mux(kiss_command)
if written != len(kiss_command): if written != len(kiss_command):
raise IOError("An IO error occurred while configuring TX power for "+str(self)) raise OSError(f"An IO error occurred while configuring TX power for {self}")
def setSpreadingFactor(self): def setSpreadingFactor(self):
sf = bytes([self.sf]) sf = bytes([self.sf])
kiss_command = bytes([KISS.FEND])+bytes([KISS.CMD_SF])+sf+bytes([KISS.FEND]) kiss_command = bytes([KISS.FEND])+bytes([KISS.CMD_SF])+sf+bytes([KISS.FEND])
written = self.write_mux(kiss_command) written = self.write_mux(kiss_command)
if written != len(kiss_command): if written != len(kiss_command):
raise IOError("An IO error occurred while configuring spreading factor for "+str(self)) raise OSError(f"An IO error occurred while configuring spreading factor for {self}")
def setCodingRate(self): def setCodingRate(self):
cr = bytes([self.cr]) cr = bytes([self.cr])
kiss_command = bytes([KISS.FEND])+bytes([KISS.CMD_CR])+cr+bytes([KISS.FEND]) kiss_command = bytes([KISS.FEND])+bytes([KISS.CMD_CR])+cr+bytes([KISS.FEND])
written = self.write_mux(kiss_command) written = self.write_mux(kiss_command)
if written != len(kiss_command): if written != len(kiss_command):
raise IOError("An IO error occurred while configuring coding rate for "+str(self)) raise OSError(f"An IO error occurred while configuring coding rate for {self}")
def setSTALock(self): def setSTALock(self):
if self.st_alock != None: if self.st_alock != None:
@ -855,7 +855,7 @@ class RNodeInterface(Interface):
kiss_command = bytes([KISS.FEND])+bytes([KISS.CMD_ST_ALOCK])+data+bytes([KISS.FEND]) kiss_command = bytes([KISS.FEND])+bytes([KISS.CMD_ST_ALOCK])+data+bytes([KISS.FEND])
written = self.write_mux(kiss_command) written = self.write_mux(kiss_command)
if written != len(kiss_command): if written != len(kiss_command):
raise IOError("An IO error occurred while configuring short-term airtime limit for "+str(self)) raise OSError(f"An IO error occurred while configuring short-term airtime limit for {self}")
def setLTALock(self): def setLTALock(self):
if self.lt_alock != None: if self.lt_alock != None:
@ -867,14 +867,14 @@ class RNodeInterface(Interface):
kiss_command = bytes([KISS.FEND])+bytes([KISS.CMD_LT_ALOCK])+data+bytes([KISS.FEND]) kiss_command = bytes([KISS.FEND])+bytes([KISS.CMD_LT_ALOCK])+data+bytes([KISS.FEND])
written = self.write_mux(kiss_command) written = self.write_mux(kiss_command)
if written != len(kiss_command): if written != len(kiss_command):
raise IOError("An IO error occurred while configuring long-term airtime limit for "+str(self)) raise OSError(f"An IO error occurred while configuring long-term airtime limit for {self}")
def setRadioState(self, state): def setRadioState(self, state):
self.state = state self.state = state
kiss_command = bytes([KISS.FEND])+bytes([KISS.CMD_RADIO_STATE])+bytes([state])+bytes([KISS.FEND]) kiss_command = bytes([KISS.FEND])+bytes([KISS.CMD_RADIO_STATE])+bytes([state])+bytes([KISS.FEND])
written = self.write_mux(kiss_command) written = self.write_mux(kiss_command)
if written != len(kiss_command): if written != len(kiss_command):
raise IOError("An IO error occurred while configuring radio state for "+str(self)) raise OSError(f"An IO error occurred while configuring radio state for {self}")
def validate_firmware(self): def validate_firmware(self):
if (self.maj_version > RNodeInterface.REQUIRED_FW_VER_MAJ): if (self.maj_version > RNodeInterface.REQUIRED_FW_VER_MAJ):
@ -887,17 +887,17 @@ class RNodeInterface(Interface):
if self.firmware_ok: if self.firmware_ok:
return return
RNS.log("The firmware version of the connected RNode is "+str(self.maj_version)+"."+str(self.min_version), RNS.LOG_ERROR) RNS.log(f"The firmware version of the connected RNode is {self.maj_version}.{self.min_version}", RNS.LOG_ERROR)
RNS.log("This version of Reticulum requires at least version "+str(RNodeInterface.REQUIRED_FW_VER_MAJ)+"."+str(RNodeInterface.REQUIRED_FW_VER_MIN), RNS.LOG_ERROR) RNS.log(f"This version of Reticulum requires at least version {RNodeInterface.REQUIRED_FW_VER_MAJ}.{RNodeInterface.REQUIRED_FW_VER_MIN}", RNS.LOG_ERROR)
RNS.log("Please update your RNode firmware with rnodeconf from https://github.com/markqvist/reticulum/") RNS.log("Please update your RNode firmware with rnodeconf from https://github.com/markqvist/reticulum/")
error_description = "The firmware version of the connected RNode is "+str(self.maj_version)+"."+str(self.min_version)+". " error_description = f"The firmware version of the connected RNode is {self.maj_version}.{self.min_version}. "
error_description += "This version of Reticulum requires at least version "+str(RNodeInterface.REQUIRED_FW_VER_MAJ)+"."+str(RNodeInterface.REQUIRED_FW_VER_MIN)+". " error_description += f"This version of Reticulum requires at least version {RNodeInterface.REQUIRED_FW_VER_MAJ}.{RNodeInterface.REQUIRED_FW_VER_MIN}. "
error_description += "Please update your RNode firmware with rnodeconf from: https://github.com/markqvist/rnodeconfigutil/" error_description += "Please update your RNode firmware with rnodeconf from: https://github.com/markqvist/rnodeconfigutil/"
self.hw_errors.append({"error": KISS.ERROR_INVALID_FIRMWARE, "description": error_description}) self.hw_errors.append({"error": KISS.ERROR_INVALID_FIRMWARE, "description": error_description})
def validateRadioState(self): def validateRadioState(self):
RNS.log("Waiting for radio configuration validation for "+str(self)+"...", RNS.LOG_VERBOSE) RNS.log(f"Waiting for radio configuration validation for {self}...", RNS.LOG_VERBOSE)
if not self.platform == KISS.PLATFORM_ESP32: if not self.platform == KISS.PLATFORM_ESP32:
sleep(1.00); sleep(1.00);
else: else:
@ -937,7 +937,7 @@ class RNodeInterface(Interface):
try: try:
self.bitrate = self.r_sf * ( (4.0/self.r_cr) / (math.pow(2,self.r_sf)/(self.r_bandwidth/1000)) ) * 1000 self.bitrate = self.r_sf * ( (4.0/self.r_cr) / (math.pow(2,self.r_sf)/(self.r_bandwidth/1000)) ) * 1000
self.bitrate_kbps = round(self.bitrate/1000.0, 2) self.bitrate_kbps = round(self.bitrate/1000.0, 2)
RNS.log(str(self)+" On-air bitrate is now "+str(self.bitrate_kbps)+ " kbps", RNS.LOG_VERBOSE) RNS.log(f"{self} On-air bitrate is now {self.bitrate_kbps} kbps", RNS.LOG_VERBOSE)
except: except:
self.bitrate = 0 self.bitrate = 0
@ -969,7 +969,7 @@ class RNodeInterface(Interface):
self.txb += datalen self.txb += datalen
if written != len(frame): if written != len(frame):
raise IOError("Serial interface only wrote "+str(written)+" bytes of "+str(len(data))) raise OSError(f"Serial interface only wrote {written} bytes of {len(data)}")
else: else:
self.queue(data) self.queue(data)
@ -1042,7 +1042,7 @@ class RNodeInterface(Interface):
command_buffer = command_buffer+bytes([byte]) command_buffer = command_buffer+bytes([byte])
if (len(command_buffer) == 4): if (len(command_buffer) == 4):
self.r_frequency = command_buffer[0] << 24 | command_buffer[1] << 16 | command_buffer[2] << 8 | command_buffer[3] self.r_frequency = command_buffer[0] << 24 | command_buffer[1] << 16 | command_buffer[2] << 8 | command_buffer[3]
RNS.log(str(self)+" Radio reporting frequency is "+str(self.r_frequency/1000000.0)+" MHz", RNS.LOG_DEBUG) RNS.log(f"{self} Radio reporting frequency is {self.r_frequency / 1000000.0} MHz", RNS.LOG_DEBUG)
self.updateBitrate() self.updateBitrate()
elif (command == KISS.CMD_BANDWIDTH): elif (command == KISS.CMD_BANDWIDTH):
@ -1058,26 +1058,26 @@ class RNodeInterface(Interface):
command_buffer = command_buffer+bytes([byte]) command_buffer = command_buffer+bytes([byte])
if (len(command_buffer) == 4): if (len(command_buffer) == 4):
self.r_bandwidth = command_buffer[0] << 24 | command_buffer[1] << 16 | command_buffer[2] << 8 | command_buffer[3] self.r_bandwidth = command_buffer[0] << 24 | command_buffer[1] << 16 | command_buffer[2] << 8 | command_buffer[3]
RNS.log(str(self)+" Radio reporting bandwidth is "+str(self.r_bandwidth/1000.0)+" KHz", RNS.LOG_DEBUG) RNS.log(f"{self} Radio reporting bandwidth is {self.r_bandwidth / 1000.0} KHz", RNS.LOG_DEBUG)
self.updateBitrate() self.updateBitrate()
elif (command == KISS.CMD_TXPOWER): elif (command == KISS.CMD_TXPOWER):
self.r_txpower = byte self.r_txpower = byte
RNS.log(str(self)+" Radio reporting TX power is "+str(self.r_txpower)+" dBm", RNS.LOG_DEBUG) RNS.log(f"{self} Radio reporting TX power is {self.r_txpower} dBm", RNS.LOG_DEBUG)
elif (command == KISS.CMD_SF): elif (command == KISS.CMD_SF):
self.r_sf = byte self.r_sf = byte
RNS.log(str(self)+" Radio reporting spreading factor is "+str(self.r_sf), RNS.LOG_DEBUG) RNS.log(f"{self} Radio reporting spreading factor is {self.r_sf}", RNS.LOG_DEBUG)
self.updateBitrate() self.updateBitrate()
elif (command == KISS.CMD_CR): elif (command == KISS.CMD_CR):
self.r_cr = byte self.r_cr = byte
RNS.log(str(self)+" Radio reporting coding rate is "+str(self.r_cr), RNS.LOG_DEBUG) RNS.log(f"{self} Radio reporting coding rate is {self.r_cr}", RNS.LOG_DEBUG)
self.updateBitrate() self.updateBitrate()
elif (command == KISS.CMD_RADIO_STATE): elif (command == KISS.CMD_RADIO_STATE):
self.r_state = byte self.r_state = byte
if self.r_state: if self.r_state:
RNS.log(str(self)+" Radio reporting state is online", RNS.LOG_DEBUG) RNS.log(f"{self} Radio reporting state is online", RNS.LOG_DEBUG)
else: else:
RNS.log(str(self)+" Radio reporting state is offline", RNS.LOG_DEBUG) RNS.log(f"{self} Radio reporting state is offline", RNS.LOG_DEBUG)
elif (command == KISS.CMD_RADIO_LOCK): elif (command == KISS.CMD_RADIO_LOCK):
self.r_lock = byte self.r_lock = byte
@ -1156,7 +1156,7 @@ class RNodeInterface(Interface):
if (len(command_buffer) == 2): if (len(command_buffer) == 2):
at = command_buffer[0] << 8 | command_buffer[1] at = command_buffer[0] << 8 | command_buffer[1]
self.r_st_alock = at/100.0 self.r_st_alock = at/100.0
RNS.log(str(self)+" Radio reporting short-term airtime limit is "+str(self.r_st_alock)+"%", RNS.LOG_DEBUG) RNS.log(f"{self} Radio reporting short-term airtime limit is {self.r_st_alock}%", RNS.LOG_DEBUG)
elif (command == KISS.CMD_LT_ALOCK): elif (command == KISS.CMD_LT_ALOCK):
if (byte == KISS.FESC): if (byte == KISS.FESC):
escape = True escape = True
@ -1171,7 +1171,7 @@ class RNodeInterface(Interface):
if (len(command_buffer) == 2): if (len(command_buffer) == 2):
at = command_buffer[0] << 8 | command_buffer[1] at = command_buffer[0] << 8 | command_buffer[1]
self.r_lt_alock = at/100.0 self.r_lt_alock = at/100.0
RNS.log(str(self)+" Radio reporting long-term airtime limit is "+str(self.r_lt_alock)+"%", RNS.LOG_DEBUG) RNS.log(f"{self} Radio reporting long-term airtime limit is {self.r_lt_alock}%", RNS.LOG_DEBUG)
elif (command == KISS.CMD_STAT_CHTM): elif (command == KISS.CMD_STAT_CHTM):
if (byte == KISS.FESC): if (byte == KISS.FESC):
escape = True escape = True
@ -1217,9 +1217,9 @@ class RNodeInterface(Interface):
self.r_preamble_symbols = prs self.r_preamble_symbols = prs
self.r_premable_time_ms = prt self.r_premable_time_ms = prt
self.r_csma_slot_time_ms = cst self.r_csma_slot_time_ms = cst
RNS.log(str(self)+" Radio reporting symbol time is "+str(round(self.r_symbol_time_ms,2))+"ms (at "+str(self.r_symbol_rate)+" baud)", RNS.LOG_DEBUG) RNS.log(f"{self} Radio reporting symbol time is {round(self.r_symbol_time_ms, 2)}ms (at {self.r_symbol_rate} baud)", RNS.LOG_DEBUG)
RNS.log(str(self)+" Radio reporting preamble is "+str(self.r_preamble_symbols)+" symbols ("+str(self.r_premable_time_ms)+"ms)", RNS.LOG_DEBUG) RNS.log(f"{self} Radio reporting preamble is {self.r_preamble_symbols} symbols ({self.r_premable_time_ms}ms)", RNS.LOG_DEBUG)
RNS.log(str(self)+" Radio reporting CSMA slot time is "+str(self.r_csma_slot_time_ms)+"ms", RNS.LOG_DEBUG) RNS.log(f"{self} Radio reporting CSMA slot time is {self.r_csma_slot_time_ms}ms", RNS.LOG_DEBUG)
elif (command == KISS.CMD_STAT_BAT): elif (command == KISS.CMD_STAT_BAT):
if (byte == KISS.FESC): if (byte == KISS.FESC):
escape = True escape = True
@ -1247,26 +1247,26 @@ class RNodeInterface(Interface):
self.mcu = byte self.mcu = byte
elif (command == KISS.CMD_ERROR): elif (command == KISS.CMD_ERROR):
if (byte == KISS.ERROR_INITRADIO): if (byte == KISS.ERROR_INITRADIO):
RNS.log(str(self)+" hardware initialisation error (code "+RNS.hexrep(byte)+")", RNS.LOG_ERROR) RNS.log(f"{self} hardware initialisation error (code {RNS.hexrep(byte)})", RNS.LOG_ERROR)
raise IOError("Radio initialisation failure") raise OSError("Radio initialisation failure")
elif (byte == KISS.ERROR_TXFAILED): elif (byte == KISS.ERROR_TXFAILED):
RNS.log(str(self)+" hardware TX error (code "+RNS.hexrep(byte)+")", RNS.LOG_ERROR) RNS.log(f"{self} hardware TX error (code {RNS.hexrep(byte)})", RNS.LOG_ERROR)
raise IOError("Hardware transmit failure") raise OSError("Hardware transmit failure")
elif (byte == KISS.ERROR_MEMORY_LOW): elif (byte == KISS.ERROR_MEMORY_LOW):
RNS.log(str(self)+" hardware error (code "+RNS.hexrep(byte)+"): Memory exhausted", RNS.LOG_ERROR) RNS.log(f"{self} hardware error (code {RNS.hexrep(byte)}): Memory exhausted", RNS.LOG_ERROR)
self.hw_errors.append({"error": KISS.ERROR_MEMORY_LOW, "description": "Memory exhausted on connected device"}) self.hw_errors.append({"error": KISS.ERROR_MEMORY_LOW, "description": "Memory exhausted on connected device"})
elif (byte == KISS.ERROR_MODEM_TIMEOUT): elif (byte == KISS.ERROR_MODEM_TIMEOUT):
RNS.log(str(self)+" hardware error (code "+RNS.hexrep(byte)+"): Modem communication timed out", RNS.LOG_ERROR) RNS.log(f"{self} hardware error (code {RNS.hexrep(byte)}): Modem communication timed out", RNS.LOG_ERROR)
self.hw_errors.append({"error": KISS.ERROR_MODEM_TIMEOUT, "description": "Modem communication timed out on connected device"}) self.hw_errors.append({"error": KISS.ERROR_MODEM_TIMEOUT, "description": "Modem communication timed out on connected device"})
else: else:
RNS.log(str(self)+" hardware error (code "+RNS.hexrep(byte)+")", RNS.LOG_ERROR) RNS.log(f"{self} hardware error (code {RNS.hexrep(byte)})", RNS.LOG_ERROR)
raise IOError("Unknown hardware failure") raise OSError("Unknown hardware failure")
elif (command == KISS.CMD_RESET): elif (command == KISS.CMD_RESET):
if (byte == 0xF8): if (byte == 0xF8):
if self.platform == KISS.PLATFORM_ESP32: if self.platform == KISS.PLATFORM_ESP32:
if self.online: if self.online:
RNS.log("Detected reset while device was online, reinitialising device...", RNS.LOG_ERROR) RNS.log("Detected reset while device was online, reinitialising device...", RNS.LOG_ERROR)
raise IOError("ESP32 reset") raise OSError("ESP32 reset")
elif (command == KISS.CMD_READY): elif (command == KISS.CMD_READY):
self.process_queue() self.process_queue()
elif (command == KISS.CMD_DETECT): elif (command == KISS.CMD_DETECT):
@ -1278,7 +1278,7 @@ class RNodeInterface(Interface):
if got == 0: if got == 0:
time_since_last = int(time.time()*1000) - last_read_ms time_since_last = int(time.time()*1000) - last_read_ms
if len(data_buffer) > 0 and time_since_last > self.timeout: if len(data_buffer) > 0 and time_since_last > self.timeout:
RNS.log(str(self)+" serial read timeout in command "+str(command), RNS.LOG_WARNING) RNS.log(f"{self} serial read timeout in command {command}", RNS.LOG_WARNING)
data_buffer = b"" data_buffer = b""
in_frame = False in_frame = False
command = KISS.CMD_UNKNOWN command = KISS.CMD_UNKNOWN
@ -1287,22 +1287,22 @@ class RNodeInterface(Interface):
if self.id_interval != None and self.id_callsign != None: if self.id_interval != None and self.id_callsign != None:
if self.first_tx != None: if self.first_tx != None:
if time.time() > self.first_tx + self.id_interval: if time.time() > self.first_tx + self.id_interval:
RNS.log("Interface "+str(self)+" is transmitting beacon data: "+str(self.id_callsign.decode("utf-8")), RNS.LOG_DEBUG) RNS.log(f"Interface {self} is transmitting beacon data: {self.id_callsign.decode('utf-8')}", RNS.LOG_DEBUG)
self.processOutgoing(self.id_callsign) self.processOutgoing(self.id_callsign)
if (time.time() - self.last_port_io > self.port_io_timeout): if (time.time() - self.last_port_io > self.port_io_timeout):
self.detect() self.detect()
if (time.time() - self.last_port_io > self.port_io_timeout*3): if (time.time() - self.last_port_io > self.port_io_timeout*3):
raise IOError("Connected port for "+str(self)+" became unresponsive") raise OSError(f"Connected port for {self} became unresponsive")
if self.bt_manager != None: if self.bt_manager != None:
sleep(0.08) sleep(0.08)
except Exception as e: except Exception as e:
self.online = False self.online = False
RNS.log("A serial port occurred, the contained exception was: "+str(e), RNS.LOG_ERROR) RNS.log(f"A serial port occurred, the contained exception was: {e}", RNS.LOG_ERROR)
RNS.log("The interface "+str(self)+" experienced an unrecoverable error and is now offline.", RNS.LOG_ERROR) RNS.log(f"The interface {self} experienced an unrecoverable error and is now offline.", RNS.LOG_ERROR)
if RNS.Reticulum.panic_on_interface_error: if RNS.Reticulum.panic_on_interface_error:
RNS.panic() RNS.panic()
@ -1330,10 +1330,10 @@ class RNodeInterface(Interface):
try: try:
time.sleep(self.reconnect_w) time.sleep(self.reconnect_w)
if self.serial != None and self.port != None: if self.serial != None and self.port != None:
RNS.log("Attempting to reconnect serial port "+str(self.port)+" for "+str(self)+"...", RNS.LOG_EXTREME) RNS.log(f"Attempting to reconnect serial port {self.port} for {self}...", RNS.LOG_EXTREME)
if self.bt_manager != None: if self.bt_manager != None:
RNS.log("Attempting to reconnect Bluetooth device for "+str(self)+"...", RNS.LOG_EXTREME) RNS.log(f"Attempting to reconnect Bluetooth device for {self}...", RNS.LOG_EXTREME)
self.open_port() self.open_port()
@ -1352,10 +1352,10 @@ class RNodeInterface(Interface):
self.enable_external_framebuffer() self.enable_external_framebuffer()
except Exception as e: except Exception as e:
RNS.log("Error while reconnecting RNode, the contained exception was: "+str(e), RNS.LOG_ERROR) RNS.log(f"Error while reconnecting RNode, the contained exception was: {e}", RNS.LOG_ERROR)
if self.online: if self.online:
RNS.log("Reconnected serial port for "+str(self)) RNS.log(f"Reconnected serial port for {self}")
def detach(self): def detach(self):
self.detached = True self.detached = True
@ -1399,7 +1399,7 @@ class RNodeInterface(Interface):
return data return data
def __str__(self): def __str__(self):
return "RNodeInterface["+str(self.name)+"]" return f"RNodeInterface[{self.name}]"
class BLEConnection(BluetoothDispatcher): class BLEConnection(BluetoothDispatcher):
UART_SERVICE_UUID = "6e400001-b5a3-f393-e0a9-e50e24dcca9e" UART_SERVICE_UUID = "6e400001-b5a3-f393-e0a9-e50e24dcca9e"
@ -1472,7 +1472,7 @@ class BLEConnection(BluetoothDispatcher):
RNS.trace_exception(e) RNS.trace_exception(e)
def __init__(self, owner=None, target_name=None, target_bt_addr=None): def __init__(self, owner=None, target_name=None, target_bt_addr=None):
super(BLEConnection, self).__init__() super().__init__()
self.owner = owner self.owner = owner
self.target_name = target_name self.target_name = target_name
self.target_bt_addr = target_bt_addr self.target_bt_addr = target_bt_addr

View File

@ -98,17 +98,17 @@ class SerialInterface(Interface):
try: try:
self.open_port() self.open_port()
except Exception as e: except Exception as e:
RNS.log("Could not open serial port for interface "+str(self), RNS.LOG_ERROR) RNS.log(f"Could not open serial port for interface {self}", RNS.LOG_ERROR)
raise e raise e
if self.serial.is_open: if self.serial.is_open:
self.configure_device() self.configure_device()
else: else:
raise IOError("Could not open serial port") raise OSError("Could not open serial port")
def open_port(self): def open_port(self):
RNS.log("Opening serial port "+self.port+"...") RNS.log(f"Opening serial port {self.port}...")
# Get device parameters # Get device parameters
from usb4a import usb from usb4a import usb
device = usb.get_usb_device(self.port) device = usb.get_usb_device(self.port)
@ -120,7 +120,7 @@ class SerialInterface(Interface):
proxy = self.pyserial.get_serial_port proxy = self.pyserial.get_serial_port
if vid == 0x1A86 and pid == 0x55D4: if vid == 0x1A86 and pid == 0x55D4:
# Force CDC driver for Qinheng CH34x # Force CDC driver for Qinheng CH34x
RNS.log(str(self)+" using CDC driver for "+RNS.hexrep(vid)+":"+RNS.hexrep(pid), RNS.LOG_DEBUG) RNS.log(f"{self} using CDC driver for {RNS.hexrep(vid)}:{RNS.hexrep(pid)}", RNS.LOG_DEBUG)
from usbserial4a.cdcacmserial4a import CdcAcmSerial from usbserial4a.cdcacmserial4a import CdcAcmSerial
proxy = CdcAcmSerial proxy = CdcAcmSerial
@ -159,9 +159,9 @@ class SerialInterface(Interface):
self.serial.USB_READ_TIMEOUT_MILLIS = 100 self.serial.USB_READ_TIMEOUT_MILLIS = 100
self.serial.timeout = 0.1 self.serial.timeout = 0.1
RNS.log(str(self)+" USB read buffer size set to "+RNS.prettysize(self.serial.DEFAULT_READ_BUFFER_SIZE), RNS.LOG_DEBUG) RNS.log(f"{self} USB read buffer size set to {RNS.prettysize(self.serial.DEFAULT_READ_BUFFER_SIZE)}", RNS.LOG_DEBUG)
RNS.log(str(self)+" USB read timeout set to "+str(self.serial.USB_READ_TIMEOUT_MILLIS)+"ms", RNS.LOG_DEBUG) RNS.log(f"{self} USB read timeout set to {self.serial.USB_READ_TIMEOUT_MILLIS}ms", RNS.LOG_DEBUG)
RNS.log(str(self)+" USB write timeout set to "+str(self.serial.USB_WRITE_TIMEOUT_MILLIS)+"ms", RNS.LOG_DEBUG) RNS.log(f"{self} USB write timeout set to {self.serial.USB_WRITE_TIMEOUT_MILLIS}ms", RNS.LOG_DEBUG)
def configure_device(self): def configure_device(self):
sleep(0.5) sleep(0.5)
@ -169,7 +169,7 @@ class SerialInterface(Interface):
thread.daemon = True thread.daemon = True
thread.start() thread.start()
self.online = True self.online = True
RNS.log("Serial port "+self.port+" is now open", RNS.LOG_VERBOSE) RNS.log(f"Serial port {self.port} is now open", RNS.LOG_VERBOSE)
def processIncoming(self, data): def processIncoming(self, data):
@ -184,7 +184,7 @@ class SerialInterface(Interface):
written = self.serial.write(data) written = self.serial.write(data)
self.txb += len(data) self.txb += len(data)
if written != len(data): if written != len(data):
raise IOError("Serial interface only wrote "+str(written)+" bytes of "+str(len(data))) raise OSError(f"Serial interface only wrote {written} bytes of {len(data)}")
def readLoop(self): def readLoop(self):
try: try:
@ -228,8 +228,8 @@ class SerialInterface(Interface):
except Exception as e: except Exception as e:
self.online = False self.online = False
RNS.log("A serial port error occurred, the contained exception was: "+str(e), RNS.LOG_ERROR) RNS.log(f"A serial port error occurred, the contained exception was: {e}", RNS.LOG_ERROR)
RNS.log("The interface "+str(self)+" experienced an unrecoverable error and is now offline.", RNS.LOG_ERROR) RNS.log(f"The interface {self} experienced an unrecoverable error and is now offline.", RNS.LOG_ERROR)
if RNS.Reticulum.panic_on_interface_error: if RNS.Reticulum.panic_on_interface_error:
RNS.panic() RNS.panic()
@ -244,17 +244,17 @@ class SerialInterface(Interface):
while not self.online: while not self.online:
try: try:
time.sleep(5) time.sleep(5)
RNS.log("Attempting to reconnect serial port "+str(self.port)+" for "+str(self)+"...", RNS.LOG_VERBOSE) RNS.log(f"Attempting to reconnect serial port {self.port} for {self}...", RNS.LOG_VERBOSE)
self.open_port() self.open_port()
if self.serial.is_open: if self.serial.is_open:
self.configure_device() self.configure_device()
except Exception as e: except Exception as e:
RNS.log("Error while reconnecting port, the contained exception was: "+str(e), RNS.LOG_ERROR) RNS.log(f"Error while reconnecting port, the contained exception was: {e}", RNS.LOG_ERROR)
RNS.log("Reconnected serial port for "+str(self)) RNS.log(f"Reconnected serial port for {self}")
def should_ingress_limit(self): def should_ingress_limit(self):
return False return False
def __str__(self): def __str__(self):
return "SerialInterface["+self.name+"]" return f"SerialInterface[{self.name}]"

View File

@ -23,5 +23,5 @@
import os import os
import glob import glob
modules = glob.glob(os.path.dirname(__file__)+"/*.py") modules = glob.glob(f"{os.path.dirname(__file__)}/*.py")
__all__ = [ os.path.basename(f)[:-3] for f in modules if not f.endswith('__init__.py')] __all__ = [ os.path.basename(f)[:-3] for f in modules if not f.endswith('__init__.py')]

View File

@ -35,7 +35,7 @@ import RNS
class AutoInterface(Interface): class AutoInterface(Interface):
DEFAULT_DISCOVERY_PORT = 29716 DEFAULT_DISCOVERY_PORT = 29716
DEFAULT_DATA_PORT = 42671 DEFAULT_DATA_PORT = 42671
DEFAULT_GROUP_ID = "reticulum".encode("utf-8") DEFAULT_GROUP_ID = b"reticulum"
SCOPE_LINK = "2" SCOPE_LINK = "2"
SCOPE_ADMIN = "4" SCOPE_ADMIN = "4"
@ -169,32 +169,32 @@ class AutoInterface(Interface):
self.group_hash = RNS.Identity.full_hash(self.group_id) self.group_hash = RNS.Identity.full_hash(self.group_id)
g = self.group_hash g = self.group_hash
#gt = "{:02x}".format(g[1]+(g[0]<<8)) #gt = f"{g[1] + (g[0] << 8):02x}"
gt = "0" gt = "0"
gt += ":"+"{:02x}".format(g[3]+(g[2]<<8)) gt += f":{g[3] + (g[2] << 8):02x}"
gt += ":"+"{:02x}".format(g[5]+(g[4]<<8)) gt += f":{g[5] + (g[4] << 8):02x}"
gt += ":"+"{:02x}".format(g[7]+(g[6]<<8)) gt += f":{g[7] + (g[6] << 8):02x}"
gt += ":"+"{:02x}".format(g[9]+(g[8]<<8)) gt += f":{g[9] + (g[8] << 8):02x}"
gt += ":"+"{:02x}".format(g[11]+(g[10]<<8)) gt += f":{g[11] + (g[10] << 8):02x}"
gt += ":"+"{:02x}".format(g[13]+(g[12]<<8)) gt += f":{g[13] + (g[12] << 8):02x}"
self.mcast_discovery_address = "ff"+self.multicast_address_type+self.discovery_scope+":"+gt self.mcast_discovery_address = f"ff{self.multicast_address_type}{self.discovery_scope}:{gt}"
suitable_interfaces = 0 suitable_interfaces = 0
for ifname in self.list_interfaces(): for ifname in self.list_interfaces():
try: try:
if RNS.vendor.platformutils.is_darwin() and ifname in AutoInterface.DARWIN_IGNORE_IFS and not ifname in self.allowed_interfaces: if RNS.vendor.platformutils.is_darwin() and ifname in AutoInterface.DARWIN_IGNORE_IFS and not ifname in self.allowed_interfaces:
RNS.log(str(self)+" skipping Darwin AWDL or tethering interface "+str(ifname), RNS.LOG_EXTREME) RNS.log(f"{self} skipping Darwin AWDL or tethering interface {ifname}", RNS.LOG_EXTREME)
elif RNS.vendor.platformutils.is_darwin() and ifname == "lo0": elif RNS.vendor.platformutils.is_darwin() and ifname == "lo0":
RNS.log(str(self)+" skipping Darwin loopback interface "+str(ifname), RNS.LOG_EXTREME) RNS.log(f"{self} skipping Darwin loopback interface {ifname}", RNS.LOG_EXTREME)
elif RNS.vendor.platformutils.is_android() and ifname in AutoInterface.ANDROID_IGNORE_IFS and not ifname in self.allowed_interfaces: elif RNS.vendor.platformutils.is_android() and ifname in AutoInterface.ANDROID_IGNORE_IFS and not ifname in self.allowed_interfaces:
RNS.log(str(self)+" skipping Android system interface "+str(ifname), RNS.LOG_EXTREME) RNS.log(f"{self} skipping Android system interface {ifname}", RNS.LOG_EXTREME)
elif ifname in self.ignored_interfaces: elif ifname in self.ignored_interfaces:
RNS.log(str(self)+" ignoring disallowed interface "+str(ifname), RNS.LOG_EXTREME) RNS.log(f"{self} ignoring disallowed interface {ifname}", RNS.LOG_EXTREME)
elif ifname in AutoInterface.ALL_IGNORE_IFS: elif ifname in AutoInterface.ALL_IGNORE_IFS:
RNS.log(str(self)+" skipping interface "+str(ifname), RNS.LOG_EXTREME) RNS.log(f"{self} skipping interface {ifname}", RNS.LOG_EXTREME)
else: else:
if len(self.allowed_interfaces) > 0 and not ifname in self.allowed_interfaces: if len(self.allowed_interfaces) > 0 and not ifname in self.allowed_interfaces:
RNS.log(str(self)+" ignoring interface "+str(ifname)+" since it was not allowed", RNS.LOG_EXTREME) RNS.log(f"{self} ignoring interface {ifname} since it was not allowed", RNS.LOG_EXTREME)
else: else:
addresses = self.list_addresses(ifname) addresses = self.list_addresses(ifname)
if self.netinfo.AF_INET6 in addresses: if self.netinfo.AF_INET6 in addresses:
@ -213,10 +213,10 @@ class AutoInterface(Interface):
RNS.log(f"{self} Selecting link-local address {link_local_addr} for interface {ifname}", RNS.LOG_EXTREME) RNS.log(f"{self} Selecting link-local address {link_local_addr} for interface {ifname}", RNS.LOG_EXTREME)
if link_local_addr == None: if link_local_addr == None:
RNS.log(str(self)+" No link-local IPv6 address configured for "+str(ifname)+", skipping interface", RNS.LOG_EXTREME) RNS.log(f"{self} No link-local IPv6 address configured for {ifname}, skipping interface", RNS.LOG_EXTREME)
else: else:
mcast_addr = self.mcast_discovery_address mcast_addr = self.mcast_discovery_address
RNS.log(str(self)+" Creating multicast discovery listener on "+str(ifname)+" with address "+str(mcast_addr), RNS.LOG_EXTREME) RNS.log(f"{self} Creating multicast discovery listener on {ifname} with address {mcast_addr}", RNS.LOG_EXTREME)
# Struct with interface index # Struct with interface index
if_struct = struct.pack("I", self.interface_name_to_index(ifname)) if_struct = struct.pack("I", self.interface_name_to_index(ifname))
@ -243,7 +243,7 @@ class AutoInterface(Interface):
else: else:
if self.discovery_scope == AutoInterface.SCOPE_LINK: if self.discovery_scope == AutoInterface.SCOPE_LINK:
addr_info = socket.getaddrinfo(mcast_addr+"%"+ifname, self.discovery_port, socket.AF_INET6, socket.SOCK_DGRAM) addr_info = socket.getaddrinfo(f"{mcast_addr}%{ifname}", self.discovery_port, socket.AF_INET6, socket.SOCK_DGRAM)
else: else:
addr_info = socket.getaddrinfo(mcast_addr, self.discovery_port, socket.AF_INET6, socket.SOCK_DGRAM) addr_info = socket.getaddrinfo(mcast_addr, self.discovery_port, socket.AF_INET6, socket.SOCK_DGRAM)
@ -267,7 +267,7 @@ class AutoInterface(Interface):
RNS.log(f"Could not configure the system interface {ifname} for use with {self}, skipping it. The contained exception was: {e}", RNS.LOG_ERROR) RNS.log(f"Could not configure the system interface {ifname} for use with {self}, skipping it. The contained exception was: {e}", RNS.LOG_ERROR)
if suitable_interfaces == 0: if suitable_interfaces == 0:
RNS.log(str(self)+" could not autoconfigure. This interface currently provides no connectivity.", RNS.LOG_WARNING) RNS.log(f"{self} could not autoconfigure. This interface currently provides no connectivity.", RNS.LOG_WARNING)
else: else:
self.receives = True self.receives = True
@ -277,13 +277,13 @@ class AutoInterface(Interface):
self.bitrate = AutoInterface.BITRATE_GUESS self.bitrate = AutoInterface.BITRATE_GUESS
peering_wait = self.announce_interval*1.2 peering_wait = self.announce_interval*1.2
RNS.log(str(self)+" discovering peers for "+str(round(peering_wait, 2))+" seconds...", RNS.LOG_VERBOSE) RNS.log(f"{self} discovering peers for {round(peering_wait, 2)} seconds...", RNS.LOG_VERBOSE)
self.owner = owner self.owner = owner
socketserver.UDPServer.address_family = socket.AF_INET6 socketserver.UDPServer.address_family = socket.AF_INET6
for ifname in self.adopted_interfaces: for ifname in self.adopted_interfaces:
local_addr = self.adopted_interfaces[ifname]+"%"+str(self.interface_name_to_index(ifname)) local_addr = f"{self.adopted_interfaces[ifname]}%{self.interface_name_to_index(ifname)}"
addr_info = socket.getaddrinfo(local_addr, self.data_port, socket.AF_INET6, socket.SOCK_DGRAM) addr_info = socket.getaddrinfo(local_addr, self.data_port, socket.AF_INET6, socket.SOCK_DGRAM)
address = addr_info[0][4] address = addr_info[0][4]
@ -317,7 +317,7 @@ class AutoInterface(Interface):
if data == expected_hash: if data == expected_hash:
self.add_peer(ipv6_src[0], ifname) self.add_peer(ipv6_src[0], ifname)
else: else:
RNS.log(str(self)+" received peering packet on "+str(ifname)+" from "+str(ipv6_src[0])+", but authentication hash was incorrect.", RNS.LOG_DEBUG) RNS.log(f"{self} received peering packet on {ifname} from {ipv6_src[0]}, but authentication hash was incorrect.", RNS.LOG_DEBUG)
def peer_jobs(self): def peer_jobs(self):
while True: while True:
@ -335,7 +335,7 @@ class AutoInterface(Interface):
# Remove any timed out peers # Remove any timed out peers
for peer_addr in timed_out_peers: for peer_addr in timed_out_peers:
removed_peer = self.peers.pop(peer_addr) removed_peer = self.peers.pop(peer_addr)
RNS.log(str(self)+" removed peer "+str(peer_addr)+" on "+str(removed_peer[0]), RNS.LOG_DEBUG) RNS.log(f"{self} removed peer {peer_addr} on {removed_peer[0]}", RNS.LOG_DEBUG)
for ifname in self.adopted_interfaces: for ifname in self.adopted_interfaces:
# Check that the link-local address has not changed # Check that the link-local address has not changed
@ -349,25 +349,25 @@ class AutoInterface(Interface):
link_local_addr = self.descope_linklocal(address["addr"]) link_local_addr = self.descope_linklocal(address["addr"])
if link_local_addr != self.adopted_interfaces[ifname]: if link_local_addr != self.adopted_interfaces[ifname]:
old_link_local_address = self.adopted_interfaces[ifname] old_link_local_address = self.adopted_interfaces[ifname]
RNS.log("Replacing link-local address "+str(old_link_local_address)+" for "+str(ifname)+" with "+str(link_local_addr), RNS.LOG_DEBUG) RNS.log(f"Replacing link-local address {old_link_local_address} for {ifname} with {link_local_addr}", RNS.LOG_DEBUG)
self.adopted_interfaces[ifname] = link_local_addr self.adopted_interfaces[ifname] = link_local_addr
self.link_local_addresses.append(link_local_addr) self.link_local_addresses.append(link_local_addr)
if old_link_local_address in self.link_local_addresses: if old_link_local_address in self.link_local_addresses:
self.link_local_addresses.remove(old_link_local_address) self.link_local_addresses.remove(old_link_local_address)
local_addr = link_local_addr+"%"+ifname local_addr = f"{link_local_addr}%{ifname}"
addr_info = socket.getaddrinfo(local_addr, self.data_port, socket.AF_INET6, socket.SOCK_DGRAM) addr_info = socket.getaddrinfo(local_addr, self.data_port, socket.AF_INET6, socket.SOCK_DGRAM)
listen_address = addr_info[0][4] listen_address = addr_info[0][4]
if ifname in self.interface_servers: if ifname in self.interface_servers:
RNS.log("Shutting down previous UDP listener for "+str(self)+" "+str(ifname), RNS.LOG_DEBUG) RNS.log(f"Shutting down previous UDP listener for {self} {ifname}", RNS.LOG_DEBUG)
previous_server = self.interface_servers[ifname] previous_server = self.interface_servers[ifname]
def shutdown_server(): def shutdown_server():
previous_server.shutdown() previous_server.shutdown()
threading.Thread(target=shutdown_server, daemon=True).start() threading.Thread(target=shutdown_server, daemon=True).start()
RNS.log("Starting new UDP listener for "+str(self)+" "+str(ifname), RNS.LOG_DEBUG) RNS.log(f"Starting new UDP listener for {self} {ifname}", RNS.LOG_DEBUG)
udp_server = socketserver.UDPServer(listen_address, self.handler_factory(self.processIncoming)) udp_server = socketserver.UDPServer(listen_address, self.handler_factory(self.processIncoming))
self.interface_servers[ifname] = udp_server self.interface_servers[ifname] = udp_server
@ -379,7 +379,7 @@ class AutoInterface(Interface):
self.carrier_changed = True self.carrier_changed = True
except Exception as e: except Exception as e:
RNS.log("Could not get device information while updating link-local addresses for "+str(self)+". The contained exception was: "+str(e), RNS.LOG_ERROR) RNS.log(f"Could not get device information while updating link-local addresses for {self}. The contained exception was: {e}", RNS.LOG_ERROR)
# Check multicast echo timeouts # Check multicast echo timeouts
last_multicast_echo = 0 last_multicast_echo = 0
@ -389,12 +389,12 @@ class AutoInterface(Interface):
if now - last_multicast_echo > self.multicast_echo_timeout: if now - last_multicast_echo > self.multicast_echo_timeout:
if ifname in self.timed_out_interfaces and self.timed_out_interfaces[ifname] == False: if ifname in self.timed_out_interfaces and self.timed_out_interfaces[ifname] == False:
self.carrier_changed = True self.carrier_changed = True
RNS.log("Multicast echo timeout for "+str(ifname)+". Carrier lost.", RNS.LOG_WARNING) RNS.log(f"Multicast echo timeout for {ifname}. Carrier lost.", RNS.LOG_WARNING)
self.timed_out_interfaces[ifname] = True self.timed_out_interfaces[ifname] = True
else: else:
if ifname in self.timed_out_interfaces and self.timed_out_interfaces[ifname] == True: if ifname in self.timed_out_interfaces and self.timed_out_interfaces[ifname] == True:
self.carrier_changed = True self.carrier_changed = True
RNS.log(str(self)+" Carrier recovered on "+str(ifname), RNS.LOG_WARNING) RNS.log(f"{self} Carrier recovered on {ifname}", RNS.LOG_WARNING)
self.timed_out_interfaces[ifname] = False self.timed_out_interfaces[ifname] = False
@ -417,7 +417,7 @@ class AutoInterface(Interface):
except Exception as e: except Exception as e:
if (ifname in self.timed_out_interfaces and self.timed_out_interfaces[ifname] == False) or not ifname in self.timed_out_interfaces: if (ifname in self.timed_out_interfaces and self.timed_out_interfaces[ifname] == False) or not ifname in self.timed_out_interfaces:
RNS.log(str(self)+" Detected possible carrier loss on "+str(ifname)+": "+str(e), RNS.LOG_WARNING) RNS.log(f"{self} Detected possible carrier loss on {ifname}: {e}", RNS.LOG_WARNING)
else: else:
pass pass
@ -431,12 +431,12 @@ class AutoInterface(Interface):
if ifname != None: if ifname != None:
self.multicast_echoes[ifname] = time.time() self.multicast_echoes[ifname] = time.time()
else: else:
RNS.log(str(self)+" received multicast echo on unexpected interface "+str(ifname), RNS.LOG_WARNING) RNS.log(f"{self} received multicast echo on unexpected interface {ifname}", RNS.LOG_WARNING)
else: else:
if not addr in self.peers: if not addr in self.peers:
self.peers[addr] = [ifname, time.time()] self.peers[addr] = [ifname, time.time()]
RNS.log(str(self)+" added peer "+str(addr)+" on "+str(ifname), RNS.LOG_DEBUG) RNS.log(f"{self} added peer {addr} on {ifname}", RNS.LOG_DEBUG)
else: else:
self.refresh_peer(addr) self.refresh_peer(addr)
@ -464,12 +464,12 @@ class AutoInterface(Interface):
if self.outbound_udp_socket == None: if self.outbound_udp_socket == None:
self.outbound_udp_socket = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM) self.outbound_udp_socket = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM)
peer_addr = str(peer)+"%"+str(self.interface_name_to_index(self.peers[peer][0])) peer_addr = f"{peer}%{self.interface_name_to_index(self.peers[peer][0])}"
addr_info = socket.getaddrinfo(peer_addr, self.data_port, socket.AF_INET6, socket.SOCK_DGRAM) addr_info = socket.getaddrinfo(peer_addr, self.data_port, socket.AF_INET6, socket.SOCK_DGRAM)
self.outbound_udp_socket.sendto(data, addr_info[0][4]) self.outbound_udp_socket.sendto(data, addr_info[0][4])
except Exception as e: except Exception as e:
RNS.log("Could not transmit on "+str(self)+". The contained exception was: "+str(e), RNS.LOG_ERROR) RNS.log(f"Could not transmit on {self}. The contained exception was: {e}", RNS.LOG_ERROR)
self.txb += len(data) self.txb += len(data)
@ -481,7 +481,7 @@ class AutoInterface(Interface):
return False return False
def __str__(self): def __str__(self):
return "AutoInterface["+self.name+"]" return f"AutoInterface[{self.name}]"
class AutoInterfaceHandler(socketserver.BaseRequestHandler): class AutoInterfaceHandler(socketserver.BaseRequestHandler):
def __init__(self, callback, *args, **keys): def __init__(self, callback, *args, **keys):

View File

@ -90,7 +90,7 @@ class I2PController:
time.sleep(0.10) time.sleep(0.10)
if self.loop == None: if self.loop == None:
RNS.log("Could not get event loop for "+str(self)+", waiting for event loop to appear", RNS.LOG_VERBOSE) RNS.log(f"Could not get event loop for {self}, waiting for event loop to appear", RNS.LOG_VERBOSE)
while self.loop == None: while self.loop == None:
self.loop = asyncio.get_event_loop() self.loop = asyncio.get_event_loop()
@ -101,7 +101,7 @@ class I2PController:
self.loop.run_forever() self.loop.run_forever()
except Exception as e: except Exception as e:
self.ready = False self.ready = False
RNS.log("Exception on event loop for "+str(self)+": "+str(e), RNS.LOG_ERROR) RNS.log(f"Exception on event loop for {self}: {e}", RNS.LOG_ERROR)
finally: finally:
self.loop.close() self.loop.close()
@ -136,7 +136,7 @@ class I2PController:
if not self.client_tunnels[i2p_destination]: if not self.client_tunnels[i2p_destination]:
try: try:
async def tunnel_up(): async def tunnel_up():
RNS.log("Bringing up I2P tunnel to "+str(owner)+", this may take a while...", RNS.LOG_INFO) RNS.log(f"Bringing up I2P tunnel to {owner}, this may take a while...", RNS.LOG_INFO)
tunnel = self.i2plib.ClientTunnel(i2p_destination, owner.local_addr, sam_address=self.sam_address, loop=self.loop) tunnel = self.i2plib.ClientTunnel(i2p_destination, owner.local_addr, sam_address=self.sam_address, loop=self.loop)
self.i2plib_tunnels[i2p_destination] = tunnel self.i2plib_tunnels[i2p_destination] = tunnel
await tunnel.run() await tunnel.run()
@ -145,7 +145,7 @@ class I2PController:
result = asyncio.run_coroutine_threadsafe(tunnel_up(), self.loop).result() result = asyncio.run_coroutine_threadsafe(tunnel_up(), self.loop).result()
if not i2p_destination in self.i2plib_tunnels: if not i2p_destination in self.i2plib_tunnels:
raise IOError("No tunnel control instance was created") raise OSError("No tunnel control instance was created")
else: else:
tn = self.i2plib_tunnels[i2p_destination] tn = self.i2plib_tunnels[i2p_destination]
@ -167,19 +167,19 @@ class I2PController:
try: try:
owner.socket.shutdown(socket.SHUT_RDWR) owner.socket.shutdown(socket.SHUT_RDWR)
except Exception as e: except Exception as e:
RNS.log("Error while shutting down socket for "+str(owner)+": "+str(e)) RNS.log(f"Error while shutting down socket for {owner}: {e}")
try: try:
owner.socket.close() owner.socket.close()
except Exception as e: except Exception as e:
RNS.log("Error while closing socket for "+str(owner)+": "+str(e)) RNS.log(f"Error while closing socket for {owner}: {e}")
self.client_tunnels[i2p_destination] = True self.client_tunnels[i2p_destination] = True
owner.awaiting_i2p_tunnel = False owner.awaiting_i2p_tunnel = False
RNS.log(str(owner)+" tunnel setup complete", RNS.LOG_VERBOSE) RNS.log(f"{owner} tunnel setup complete", RNS.LOG_VERBOSE)
else: else:
raise IOError("Got no status response from SAM API") raise OSError("Got no status response from SAM API")
except ConnectionRefusedError as e: except ConnectionRefusedError as e:
raise e raise e
@ -188,7 +188,7 @@ class I2PController:
raise e raise e
except Exception as e: except Exception as e:
RNS.log("Unexpected error type from I2P SAM: "+str(e), RNS.LOG_ERROR) RNS.log(f"Unexpected error type from I2P SAM: {e}", RNS.LOG_ERROR)
raise e raise e
else: else:
@ -197,16 +197,16 @@ class I2PController:
i2p_exception = i2ptunnel.status["exception"] i2p_exception = i2ptunnel.status["exception"]
if i2ptunnel.status["setup_ran"] == False: if i2ptunnel.status["setup_ran"] == False:
RNS.log(str(self)+" I2P tunnel setup did not complete", RNS.LOG_ERROR) RNS.log(f"{self} I2P tunnel setup did not complete", RNS.LOG_ERROR)
self.stop_tunnel(i2ptunnel) self.stop_tunnel(i2ptunnel)
return False return False
elif i2p_exception != None: elif i2p_exception != None:
RNS.log("An error ocurred while setting up I2P tunnel to "+str(i2p_destination), RNS.LOG_ERROR) RNS.log(f"An error ocurred while setting up I2P tunnel to {i2p_destination}", RNS.LOG_ERROR)
if isinstance(i2p_exception, RNS.vendor.i2plib.exceptions.CantReachPeer): if isinstance(i2p_exception, RNS.vendor.i2plib.exceptions.CantReachPeer):
RNS.log("The I2P daemon can't reach peer "+str(i2p_destination), RNS.LOG_ERROR) RNS.log(f"The I2P daemon can't reach peer {i2p_destination}", RNS.LOG_ERROR)
elif isinstance(i2p_exception, RNS.vendor.i2plib.exceptions.DuplicatedDest): elif isinstance(i2p_exception, RNS.vendor.i2plib.exceptions.DuplicatedDest):
RNS.log("The I2P daemon reported that the destination is already in use", RNS.LOG_ERROR) RNS.log("The I2P daemon reported that the destination is already in use", RNS.LOG_ERROR)
@ -218,19 +218,19 @@ class I2PController:
RNS.log("The I2P daemon reported that the stream session ID doesn't exist", RNS.LOG_ERROR) RNS.log("The I2P daemon reported that the stream session ID doesn't exist", RNS.LOG_ERROR)
elif isinstance(i2p_exception, RNS.vendor.i2plib.exceptions.InvalidKey): elif isinstance(i2p_exception, RNS.vendor.i2plib.exceptions.InvalidKey):
RNS.log("The I2P daemon reported that the key for "+str(i2p_destination)+" is invalid", RNS.LOG_ERROR) RNS.log(f"The I2P daemon reported that the key for {i2p_destination} is invalid", RNS.LOG_ERROR)
elif isinstance(i2p_exception, RNS.vendor.i2plib.exceptions.KeyNotFound): elif isinstance(i2p_exception, RNS.vendor.i2plib.exceptions.KeyNotFound):
RNS.log("The I2P daemon could not find the key for "+str(i2p_destination), RNS.LOG_ERROR) RNS.log(f"The I2P daemon could not find the key for {i2p_destination}", RNS.LOG_ERROR)
elif isinstance(i2p_exception, RNS.vendor.i2plib.exceptions.PeerNotFound): elif isinstance(i2p_exception, RNS.vendor.i2plib.exceptions.PeerNotFound):
RNS.log("The I2P daemon mould not find the peer "+str(i2p_destination), RNS.LOG_ERROR) RNS.log(f"The I2P daemon mould not find the peer {i2p_destination}", RNS.LOG_ERROR)
elif isinstance(i2p_exception, RNS.vendor.i2plib.exceptions.I2PError): elif isinstance(i2p_exception, RNS.vendor.i2plib.exceptions.I2PError):
RNS.log("The I2P daemon experienced an unspecified error", RNS.LOG_ERROR) RNS.log("The I2P daemon experienced an unspecified error", RNS.LOG_ERROR)
elif isinstance(i2p_exception, RNS.vendor.i2plib.exceptions.Timeout): elif isinstance(i2p_exception, RNS.vendor.i2plib.exceptions.Timeout):
RNS.log("I2P daemon timed out while setting up client tunnel to "+str(i2p_destination), RNS.LOG_ERROR) RNS.log(f"I2P daemon timed out while setting up client tunnel to {i2p_destination}", RNS.LOG_ERROR)
RNS.log("Resetting I2P tunnel and retrying later", RNS.LOG_ERROR) RNS.log("Resetting I2P tunnel and retrying later", RNS.LOG_ERROR)
@ -238,13 +238,13 @@ class I2PController:
return False return False
elif i2ptunnel.status["setup_failed"] == True: elif i2ptunnel.status["setup_failed"] == True:
RNS.log(str(self)+" Unspecified I2P tunnel setup error, resetting I2P tunnel", RNS.LOG_ERROR) RNS.log(f"{self} Unspecified I2P tunnel setup error, resetting I2P tunnel", RNS.LOG_ERROR)
self.stop_tunnel(i2ptunnel) self.stop_tunnel(i2ptunnel)
return False return False
else: else:
RNS.log(str(self)+" Got no status from SAM API, resetting I2P tunnel", RNS.LOG_ERROR) RNS.log(f"{self} Got no status from SAM API, resetting I2P tunnel", RNS.LOG_ERROR)
self.stop_tunnel(i2ptunnel) self.stop_tunnel(i2ptunnel)
return False return False
@ -259,11 +259,11 @@ class I2PController:
# Old format # Old format
i2p_dest_hash_of = RNS.Identity.full_hash(RNS.Identity.full_hash(owner.name.encode("utf-8"))) i2p_dest_hash_of = RNS.Identity.full_hash(RNS.Identity.full_hash(owner.name.encode("utf-8")))
i2p_keyfile_of = self.storagepath+"/"+RNS.hexrep(i2p_dest_hash_of, delimit=False)+".i2p" i2p_keyfile_of = f"{self.storagepath}/{RNS.hexrep(i2p_dest_hash_of, delimit=False)}.i2p"
# New format # New format
i2p_dest_hash_nf = RNS.Identity.full_hash(RNS.Identity.full_hash(owner.name.encode("utf-8"))+RNS.Identity.full_hash(RNS.Transport.identity.hash)) i2p_dest_hash_nf = RNS.Identity.full_hash(RNS.Identity.full_hash(owner.name.encode("utf-8"))+RNS.Identity.full_hash(RNS.Transport.identity.hash))
i2p_keyfile_nf = self.storagepath+"/"+RNS.hexrep(i2p_dest_hash_nf, delimit=False)+".i2p" i2p_keyfile_nf = f"{self.storagepath}/{RNS.hexrep(i2p_dest_hash_nf, delimit=False)}.i2p"
# Use old format if a key is already present # Use old format if a key is already present
if os.path.isfile(i2p_keyfile_of): if os.path.isfile(i2p_keyfile_of):
@ -279,7 +279,7 @@ class I2PController:
key_file.write(i2p_dest.private_key.base64) key_file.write(i2p_dest.private_key.base64)
key_file.close() key_file.close()
else: else:
key_file = open(i2p_keyfile, "r") key_file = open(i2p_keyfile)
prvd = key_file.read() prvd = key_file.read()
key_file.close() key_file.close()
i2p_dest = self.i2plib.Destination(data=prvd, has_private_key=True) i2p_dest = self.i2plib.Destination(data=prvd, has_private_key=True)
@ -294,12 +294,12 @@ class I2PController:
if self.server_tunnels[i2p_b32] == False: if self.server_tunnels[i2p_b32] == False:
try: try:
async def tunnel_up(): async def tunnel_up():
RNS.log(str(owner)+" Bringing up I2P endpoint, this may take a while...", RNS.LOG_INFO) RNS.log(f"{owner} Bringing up I2P endpoint, this may take a while...", RNS.LOG_INFO)
tunnel = self.i2plib.ServerTunnel((owner.bind_ip, owner.bind_port), loop=self.loop, destination=i2p_dest, sam_address=self.sam_address) tunnel = self.i2plib.ServerTunnel((owner.bind_ip, owner.bind_port), loop=self.loop, destination=i2p_dest, sam_address=self.sam_address)
self.i2plib_tunnels[i2p_b32] = tunnel self.i2plib_tunnels[i2p_b32] = tunnel
await tunnel.run() await tunnel.run()
owner.online = True owner.online = True
RNS.log(str(owner)+ " endpoint setup complete. Now reachable at: "+str(i2p_dest.base32)+".b32.i2p", RNS.LOG_VERBOSE) RNS.log(f"{owner} endpoint setup complete. Now reachable at: {i2p_dest.base32}.b32.i2p", RNS.LOG_VERBOSE)
asyncio.run_coroutine_threadsafe(tunnel_up(), self.loop).result() asyncio.run_coroutine_threadsafe(tunnel_up(), self.loop).result()
self.server_tunnels[i2p_b32] = True self.server_tunnels[i2p_b32] = True
@ -313,7 +313,7 @@ class I2PController:
i2p_exception = i2ptunnel.status["exception"] i2p_exception = i2ptunnel.status["exception"]
if i2ptunnel.status["setup_ran"] == False: if i2ptunnel.status["setup_ran"] == False:
RNS.log(str(self)+" I2P tunnel setup did not complete", RNS.LOG_ERROR) RNS.log(f"{self} I2P tunnel setup did not complete", RNS.LOG_ERROR)
self.stop_tunnel(i2ptunnel) self.stop_tunnel(i2ptunnel)
return False return False
@ -322,7 +322,7 @@ class I2PController:
RNS.log("An error ocurred while setting up I2P tunnel", RNS.LOG_ERROR) RNS.log("An error ocurred while setting up I2P tunnel", RNS.LOG_ERROR)
if isinstance(i2p_exception, RNS.vendor.i2plib.exceptions.CantReachPeer): if isinstance(i2p_exception, RNS.vendor.i2plib.exceptions.CantReachPeer):
RNS.log("The I2P daemon can't reach peer "+str(i2p_destination), RNS.LOG_ERROR) RNS.log(f"The I2P daemon can't reach peer {i2p_destination}", RNS.LOG_ERROR)
elif isinstance(i2p_exception, RNS.vendor.i2plib.exceptions.DuplicatedDest): elif isinstance(i2p_exception, RNS.vendor.i2plib.exceptions.DuplicatedDest):
RNS.log("The I2P daemon reported that the destination is already in use", RNS.LOG_ERROR) RNS.log("The I2P daemon reported that the destination is already in use", RNS.LOG_ERROR)
@ -334,19 +334,19 @@ class I2PController:
RNS.log("The I2P daemon reported that the stream session ID doesn't exist", RNS.LOG_ERROR) RNS.log("The I2P daemon reported that the stream session ID doesn't exist", RNS.LOG_ERROR)
elif isinstance(i2p_exception, RNS.vendor.i2plib.exceptions.InvalidKey): elif isinstance(i2p_exception, RNS.vendor.i2plib.exceptions.InvalidKey):
RNS.log("The I2P daemon reported that the key for "+str(i2p_destination)+" is invalid", RNS.LOG_ERROR) RNS.log(f"The I2P daemon reported that the key for {i2p_destination} is invalid", RNS.LOG_ERROR)
elif isinstance(i2p_exception, RNS.vendor.i2plib.exceptions.KeyNotFound): elif isinstance(i2p_exception, RNS.vendor.i2plib.exceptions.KeyNotFound):
RNS.log("The I2P daemon could not find the key for "+str(i2p_destination), RNS.LOG_ERROR) RNS.log(f"The I2P daemon could not find the key for {i2p_destination}", RNS.LOG_ERROR)
elif isinstance(i2p_exception, RNS.vendor.i2plib.exceptions.PeerNotFound): elif isinstance(i2p_exception, RNS.vendor.i2plib.exceptions.PeerNotFound):
RNS.log("The I2P daemon mould not find the peer "+str(i2p_destination), RNS.LOG_ERROR) RNS.log(f"The I2P daemon mould not find the peer {i2p_destination}", RNS.LOG_ERROR)
elif isinstance(i2p_exception, RNS.vendor.i2plib.exceptions.I2PError): elif isinstance(i2p_exception, RNS.vendor.i2plib.exceptions.I2PError):
RNS.log("The I2P daemon experienced an unspecified error", RNS.LOG_ERROR) RNS.log("The I2P daemon experienced an unspecified error", RNS.LOG_ERROR)
elif isinstance(i2p_exception, RNS.vendor.i2plib.exceptions.Timeout): elif isinstance(i2p_exception, RNS.vendor.i2plib.exceptions.Timeout):
RNS.log("I2P daemon timed out while setting up client tunnel to "+str(i2p_destination), RNS.LOG_ERROR) RNS.log(f"I2P daemon timed out while setting up client tunnel to {i2p_destination}", RNS.LOG_ERROR)
RNS.log("Resetting I2P tunnel and retrying later", RNS.LOG_ERROR) RNS.log("Resetting I2P tunnel and retrying later", RNS.LOG_ERROR)
@ -354,13 +354,13 @@ class I2PController:
return False return False
elif i2ptunnel.status["setup_failed"] == True: elif i2ptunnel.status["setup_failed"] == True:
RNS.log(str(self)+" Unspecified I2P tunnel setup error, resetting I2P tunnel", RNS.LOG_ERROR) RNS.log(f"{self} Unspecified I2P tunnel setup error, resetting I2P tunnel", RNS.LOG_ERROR)
self.stop_tunnel(i2ptunnel) self.stop_tunnel(i2ptunnel)
return False return False
else: else:
RNS.log(str(self)+" Got no status from SAM API, resetting I2P tunnel", RNS.LOG_ERROR) RNS.log(f"{self} Got no status from SAM API, resetting I2P tunnel", RNS.LOG_ERROR)
self.stop_tunnel(i2ptunnel) self.stop_tunnel(i2ptunnel)
return False return False
@ -475,11 +475,11 @@ class I2PInterfacePeer(Interface):
self.target_port = self.bind_port self.target_port = self.bind_port
if not self.parent_interface.i2p.client_tunnel(self, target_i2p_dest): if not self.parent_interface.i2p.client_tunnel(self, target_i2p_dest):
RNS.log(str(self)+" I2P control process experienced an error, requesting new tunnel...", RNS.LOG_ERROR) RNS.log(f"{self} I2P control process experienced an error, requesting new tunnel...", RNS.LOG_ERROR)
self.awaiting_i2p_tunnel = True self.awaiting_i2p_tunnel = True
except Exception as e: except Exception as e:
RNS.log("Error while while configuring "+str(self)+": "+str(e), RNS.LOG_ERROR) RNS.log(f"Error while while configuring {self}: {e}", RNS.LOG_ERROR)
RNS.log("Check that I2P is installed and running, and that SAM is enabled. Retrying tunnel setup later.", RNS.LOG_ERROR) RNS.log("Check that I2P is installed and running, and that SAM is enabled. Retrying tunnel setup later.", RNS.LOG_ERROR)
time.sleep(8) time.sleep(8)
@ -532,16 +532,16 @@ class I2PInterfacePeer(Interface):
if socket != None: if socket != None:
target_socket.shutdown(socket.SHUT_RDWR) target_socket.shutdown(socket.SHUT_RDWR)
except Exception as e: except Exception as e:
RNS.log("Error while shutting down socket for "+str(self)+": "+str(e)) RNS.log(f"Error while shutting down socket for {self}: {e}")
try: try:
if socket != None: if socket != None:
target_socket.close() target_socket.close()
except Exception as e: except Exception as e:
RNS.log("Error while closing socket for "+str(self)+": "+str(e)) RNS.log(f"Error while closing socket for {self}: {e}")
def detach(self): def detach(self):
RNS.log("Detaching "+str(self), RNS.LOG_DEBUG) RNS.log(f"Detaching {self}", RNS.LOG_DEBUG)
if self.socket != None: if self.socket != None:
if hasattr(self.socket, "close"): if hasattr(self.socket, "close"):
if callable(self.socket.close): if callable(self.socket.close):
@ -550,12 +550,12 @@ class I2PInterfacePeer(Interface):
try: try:
self.socket.shutdown(socket.SHUT_RDWR) self.socket.shutdown(socket.SHUT_RDWR)
except Exception as e: except Exception as e:
RNS.log("Error while shutting down socket for "+str(self)+": "+str(e)) RNS.log(f"Error while shutting down socket for {self}: {e}")
try: try:
self.socket.close() self.socket.close()
except Exception as e: except Exception as e:
RNS.log("Error while closing socket for "+str(self)+": "+str(e)) RNS.log(f"Error while closing socket for {self}: {e}")
self.socket = None self.socket = None
@ -568,8 +568,8 @@ class I2PInterfacePeer(Interface):
except Exception as e: except Exception as e:
if initial: if initial:
if not self.awaiting_i2p_tunnel: if not self.awaiting_i2p_tunnel:
RNS.log("Initial connection for "+str(self)+" could not be established: "+str(e), RNS.LOG_ERROR) RNS.log(f"Initial connection for {self} could not be established: {e}", RNS.LOG_ERROR)
RNS.log("Leaving unconnected and retrying connection in "+str(I2PInterfacePeer.RECONNECT_WAIT)+" seconds.", RNS.LOG_ERROR) RNS.log(f"Leaving unconnected and retrying connection in {I2PInterfacePeer.RECONNECT_WAIT} seconds.", RNS.LOG_ERROR)
return False return False
@ -600,7 +600,7 @@ class I2PInterfacePeer(Interface):
attempts += 1 attempts += 1
if self.max_reconnect_tries != None and attempts > self.max_reconnect_tries: if self.max_reconnect_tries != None and attempts > self.max_reconnect_tries:
RNS.log("Max reconnection attempts reached for "+str(self), RNS.LOG_ERROR) RNS.log(f"Max reconnection attempts reached for {self}", RNS.LOG_ERROR)
self.teardown() self.teardown()
break break
@ -609,12 +609,12 @@ class I2PInterfacePeer(Interface):
except Exception as e: except Exception as e:
if not self.awaiting_i2p_tunnel: if not self.awaiting_i2p_tunnel:
RNS.log("Connection attempt for "+str(self)+" failed: "+str(e), RNS.LOG_DEBUG) RNS.log(f"Connection attempt for {self} failed: {e}", RNS.LOG_DEBUG)
else: else:
RNS.log(str(self)+" still waiting for I2P tunnel to appear", RNS.LOG_VERBOSE) RNS.log(f"{self} still waiting for I2P tunnel to appear", RNS.LOG_VERBOSE)
if not self.never_connected: if not self.never_connected:
RNS.log(str(self)+" Re-established connection via I2P tunnel", RNS.LOG_INFO) RNS.log(f"{self} Re-established connection via I2P tunnel", RNS.LOG_INFO)
self.reconnecting = False self.reconnecting = False
thread = threading.Thread(target=self.read_loop) thread = threading.Thread(target=self.read_loop)
@ -625,7 +625,7 @@ class I2PInterfacePeer(Interface):
else: else:
RNS.log("Attempt to reconnect on a non-initiator I2P interface. This should not happen.", RNS.LOG_ERROR) RNS.log("Attempt to reconnect on a non-initiator I2P interface. This should not happen.", RNS.LOG_ERROR)
raise IOError("Attempt to reconnect on a non-initiator I2P interface") raise OSError("Attempt to reconnect on a non-initiator I2P interface")
def processIncoming(self, data): def processIncoming(self, data):
self.rxb += len(data) self.rxb += len(data)
@ -656,8 +656,8 @@ class I2PInterfacePeer(Interface):
self.parent_interface.txb += len(data) self.parent_interface.txb += len(data)
except Exception as e: except Exception as e:
RNS.log("Exception occurred while transmitting via "+str(self)+", tearing down interface", RNS.LOG_ERROR) RNS.log(f"Exception occurred while transmitting via {self}, tearing down interface", RNS.LOG_ERROR)
RNS.log("The contained exception was: "+str(e), RNS.LOG_ERROR) RNS.log(f"The contained exception was: {e}", RNS.LOG_ERROR)
self.teardown() self.teardown()
@ -683,7 +683,7 @@ class I2PInterfacePeer(Interface):
if self.socket != None: if self.socket != None:
self.socket.sendall(bytes([HDLC.FLAG, HDLC.FLAG])) self.socket.sendall(bytes([HDLC.FLAG, HDLC.FLAG]))
except Exception as e: except Exception as e:
RNS.log("An error ocurred while sending I2P keepalive. The contained exception was: "+str(e), RNS.LOG_ERROR) RNS.log(f"An error ocurred while sending I2P keepalive. The contained exception was: {e}", RNS.LOG_ERROR)
self.shutdown_socket(self.socket) self.shutdown_socket(self.socket)
should_run = False should_run = False
@ -693,12 +693,12 @@ class I2PInterfacePeer(Interface):
try: try:
self.socket.shutdown(socket.SHUT_RDWR) self.socket.shutdown(socket.SHUT_RDWR)
except Exception as e: except Exception as e:
RNS.log("Error while shutting down socket for "+str(self)+": "+str(e)) RNS.log(f"Error while shutting down socket for {self}: {e}")
try: try:
self.socket.close() self.socket.close()
except Exception as e: except Exception as e:
RNS.log("Error while closing socket for "+str(self)+": "+str(e)) RNS.log(f"Error while closing socket for {self}: {e}")
should_run = False should_run = False
@ -782,10 +782,10 @@ class I2PInterfacePeer(Interface):
self.wd_reset = False self.wd_reset = False
if self.initiator and not self.detached: if self.initiator and not self.detached:
RNS.log("Socket for "+str(self)+" was closed, attempting to reconnect...", RNS.LOG_WARNING) RNS.log(f"Socket for {self} was closed, attempting to reconnect...", RNS.LOG_WARNING)
self.reconnect() self.reconnect()
else: else:
RNS.log("Socket for remote client "+str(self)+" was closed.", RNS.LOG_VERBOSE) RNS.log(f"Socket for remote client {self} was closed.", RNS.LOG_VERBOSE)
self.teardown() self.teardown()
break break
@ -793,7 +793,7 @@ class I2PInterfacePeer(Interface):
except Exception as e: except Exception as e:
self.online = False self.online = False
RNS.log("An interface error occurred for "+str(self)+", the contained exception was: "+str(e), RNS.LOG_WARNING) RNS.log(f"An interface error occurred for {self}, the contained exception was: {e}", RNS.LOG_WARNING)
if self.initiator: if self.initiator:
RNS.log("Attempting to reconnect...", RNS.LOG_WARNING) RNS.log("Attempting to reconnect...", RNS.LOG_WARNING)
@ -803,12 +803,12 @@ class I2PInterfacePeer(Interface):
def teardown(self): def teardown(self):
if self.initiator and not self.detached: if self.initiator and not self.detached:
RNS.log("The interface "+str(self)+" experienced an unrecoverable error and is being torn down. Restart Reticulum to attempt to open this interface again.", RNS.LOG_ERROR) RNS.log(f"The interface {self} experienced an unrecoverable error and is being torn down. Restart Reticulum to attempt to open this interface again.", RNS.LOG_ERROR)
if RNS.Reticulum.panic_on_interface_error: if RNS.Reticulum.panic_on_interface_error:
RNS.panic() RNS.panic()
else: else:
RNS.log("The interface "+str(self)+" is being torn down.", RNS.LOG_VERBOSE) RNS.log(f"The interface {self} is being torn down.", RNS.LOG_VERBOSE)
self.online = False self.online = False
self.OUT = False self.OUT = False
@ -824,7 +824,7 @@ class I2PInterfacePeer(Interface):
def __str__(self): def __str__(self):
return "I2PInterfacePeer["+str(self.name)+"]" return f"I2PInterfacePeer[{self.name}]"
class I2PInterface(Interface): class I2PInterface(Interface):
@ -895,11 +895,11 @@ class I2PInterface(Interface):
while True: while True:
try: try:
if not self.i2p.server_tunnel(self): if not self.i2p.server_tunnel(self):
RNS.log(str(self)+" I2P control process experienced an error, requesting new tunnel...", RNS.LOG_ERROR) RNS.log(f"{self} I2P control process experienced an error, requesting new tunnel...", RNS.LOG_ERROR)
self.online = False self.online = False
except Exception as e: except Exception as e:
RNS.log("Error while while configuring "+str(self)+": "+str(e), RNS.LOG_ERROR) RNS.log(f"Error while while configuring {self}: {e}", RNS.LOG_ERROR)
RNS.log("Check that I2P is installed and running, and that SAM is enabled. Retrying tunnel setup later.", RNS.LOG_ERROR) RNS.log("Check that I2P is installed and running, and that SAM is enabled. Retrying tunnel setup later.", RNS.LOG_ERROR)
time.sleep(15) time.sleep(15)
@ -911,7 +911,7 @@ class I2PInterface(Interface):
if peers != None: if peers != None:
for peer_addr in peers: for peer_addr in peers:
interface_name = self.name+" to "+peer_addr interface_name = f"{self.name} to {peer_addr}"
peer_interface = I2PInterfacePeer(self, self.owner, interface_name, peer_addr) peer_interface = I2PInterfacePeer(self, self.owner, interface_name, peer_addr)
peer_interface.OUT = True peer_interface.OUT = True
peer_interface.IN = True peer_interface.IN = True
@ -921,7 +921,7 @@ class I2PInterface(Interface):
def incoming_connection(self, handler): def incoming_connection(self, handler):
RNS.log("Accepting incoming I2P connection", RNS.LOG_VERBOSE) RNS.log("Accepting incoming I2P connection", RNS.LOG_VERBOSE)
interface_name = "Connected peer on "+self.name interface_name = f"Connected peer on {self.name}"
spawned_interface = I2PInterfacePeer(self, self.owner, interface_name, connected_socket=handler.request) spawned_interface = I2PInterfacePeer(self, self.owner, interface_name, connected_socket=handler.request)
spawned_interface.OUT = True spawned_interface.OUT = True
spawned_interface.IN = True spawned_interface.IN = True
@ -954,7 +954,7 @@ class I2PInterface(Interface):
spawned_interface.announce_rate_penalty = self.announce_rate_penalty spawned_interface.announce_rate_penalty = self.announce_rate_penalty
spawned_interface.mode = self.mode spawned_interface.mode = self.mode
spawned_interface.HW_MTU = self.HW_MTU spawned_interface.HW_MTU = self.HW_MTU
RNS.log("Spawned new I2PInterface Peer: "+str(spawned_interface), RNS.LOG_VERBOSE) RNS.log(f"Spawned new I2PInterface Peer: {spawned_interface}", RNS.LOG_VERBOSE)
RNS.Transport.interfaces.append(spawned_interface) RNS.Transport.interfaces.append(spawned_interface)
self.clients += 1 self.clients += 1
spawned_interface.read_loop() spawned_interface.read_loop()
@ -969,11 +969,11 @@ class I2PInterface(Interface):
if from_spawned: self.oa_freq_deque.append(time.time()) if from_spawned: self.oa_freq_deque.append(time.time())
def detach(self): def detach(self):
RNS.log("Detaching "+str(self), RNS.LOG_DEBUG) RNS.log(f"Detaching {self}", RNS.LOG_DEBUG)
self.i2p.stop() self.i2p.stop()
def __str__(self): def __str__(self):
return "I2PInterface["+self.name+"]" return f"I2PInterface[{self.name}]"
class I2PInterfaceHandler(socketserver.BaseRequestHandler): class I2PInterfaceHandler(socketserver.BaseRequestHandler):
def __init__(self, callback, *args, **keys): def __init__(self, callback, *args, **keys):

View File

@ -140,7 +140,7 @@ class Interface:
selected_announce_packet = announce_packet selected_announce_packet = announce_packet
if selected_announce_packet != None: if selected_announce_packet != None:
RNS.log("Releasing held announce packet "+str(selected_announce_packet)+" from "+str(self), RNS.LOG_EXTREME) RNS.log(f"Releasing held announce packet {selected_announce_packet} from {self}", RNS.LOG_EXTREME)
self.ic_held_release = time.time() + self.ic_held_release_interval self.ic_held_release = time.time() + self.ic_held_release_interval
self.held_announces.pop(selected_announce_packet.destination_hash) self.held_announces.pop(selected_announce_packet.destination_hash)
def release(): def release():
@ -148,8 +148,8 @@ class Interface:
threading.Thread(target=release, daemon=True).start() threading.Thread(target=release, daemon=True).start()
except Exception as e: except Exception as e:
RNS.log("An error occurred while processing held announces for "+str(self), RNS.LOG_ERROR) RNS.log(f"An error occurred while processing held announces for {self}", RNS.LOG_ERROR)
RNS.log("The contained exception was: "+str(e), RNS.LOG_ERROR) RNS.log(f"The contained exception was: {e}", RNS.LOG_ERROR)
def received_announce(self): def received_announce(self):
self.ia_freq_deque.append(time.time()) self.ia_freq_deque.append(time.time())
@ -234,7 +234,7 @@ class Interface:
except Exception as e: except Exception as e:
self.announce_queue = [] self.announce_queue = []
RNS.log("Error while processing announce queue on "+str(self)+". The contained exception was: "+str(e), RNS.LOG_ERROR) RNS.log(f"Error while processing announce queue on {self}. The contained exception was: {e}", RNS.LOG_ERROR)
RNS.log("The announce queue for this interface has been cleared.", RNS.LOG_ERROR) RNS.log("The announce queue for this interface has been cleared.", RNS.LOG_ERROR)
def detach(self): def detach(self):

View File

@ -113,17 +113,17 @@ class KISSInterface(Interface):
try: try:
self.open_port() self.open_port()
except Exception as e: except Exception as e:
RNS.log("Could not open serial port "+self.port, RNS.LOG_ERROR) RNS.log(f"Could not open serial port {self.port}", RNS.LOG_ERROR)
raise e raise e
if self.serial.is_open: if self.serial.is_open:
self.configure_device() self.configure_device()
else: else:
raise IOError("Could not open serial port") raise OSError("Could not open serial port")
def open_port(self): def open_port(self):
RNS.log("Opening serial port "+self.port+"...", RNS.LOG_VERBOSE) RNS.log(f"Opening serial port {self.port}...", RNS.LOG_VERBOSE)
self.serial = self.pyserial.Serial( self.serial = self.pyserial.Serial(
port = self.port, port = self.port,
baudrate = self.speed, baudrate = self.speed,
@ -146,7 +146,7 @@ class KISSInterface(Interface):
thread.daemon = True thread.daemon = True
thread.start() thread.start()
self.online = True self.online = True
RNS.log("Serial port "+self.port+" is now open") RNS.log(f"Serial port {self.port} is now open")
RNS.log("Configuring KISS interface parameters...") RNS.log("Configuring KISS interface parameters...")
self.setPreamble(self.preamble) self.setPreamble(self.preamble)
self.setTxTail(self.txtail) self.setTxTail(self.txtail)
@ -168,7 +168,7 @@ class KISSInterface(Interface):
kiss_command = bytes([KISS.FEND])+bytes([KISS.CMD_TXDELAY])+bytes([preamble])+bytes([KISS.FEND]) kiss_command = bytes([KISS.FEND])+bytes([KISS.CMD_TXDELAY])+bytes([preamble])+bytes([KISS.FEND])
written = self.serial.write(kiss_command) written = self.serial.write(kiss_command)
if written != len(kiss_command): if written != len(kiss_command):
raise IOError("Could not configure KISS interface preamble to "+str(preamble_ms)+" (command value "+str(preamble)+")") raise OSError(f"Could not configure KISS interface preamble to {preamble_ms} (command value {preamble})")
def setTxTail(self, txtail): def setTxTail(self, txtail):
txtail_ms = txtail txtail_ms = txtail
@ -181,7 +181,7 @@ class KISSInterface(Interface):
kiss_command = bytes([KISS.FEND])+bytes([KISS.CMD_TXTAIL])+bytes([txtail])+bytes([KISS.FEND]) kiss_command = bytes([KISS.FEND])+bytes([KISS.CMD_TXTAIL])+bytes([txtail])+bytes([KISS.FEND])
written = self.serial.write(kiss_command) written = self.serial.write(kiss_command)
if written != len(kiss_command): if written != len(kiss_command):
raise IOError("Could not configure KISS interface TX tail to "+str(txtail_ms)+" (command value "+str(txtail)+")") raise OSError(f"Could not configure KISS interface TX tail to {txtail_ms} (command value {txtail})")
def setPersistence(self, persistence): def setPersistence(self, persistence):
if persistence < 0: if persistence < 0:
@ -192,7 +192,7 @@ class KISSInterface(Interface):
kiss_command = bytes([KISS.FEND])+bytes([KISS.CMD_P])+bytes([persistence])+bytes([KISS.FEND]) kiss_command = bytes([KISS.FEND])+bytes([KISS.CMD_P])+bytes([persistence])+bytes([KISS.FEND])
written = self.serial.write(kiss_command) written = self.serial.write(kiss_command)
if written != len(kiss_command): if written != len(kiss_command):
raise IOError("Could not configure KISS interface persistence to "+str(persistence)) raise OSError(f"Could not configure KISS interface persistence to {persistence}")
def setSlotTime(self, slottime): def setSlotTime(self, slottime):
slottime_ms = slottime slottime_ms = slottime
@ -205,16 +205,16 @@ class KISSInterface(Interface):
kiss_command = bytes([KISS.FEND])+bytes([KISS.CMD_SLOTTIME])+bytes([slottime])+bytes([KISS.FEND]) kiss_command = bytes([KISS.FEND])+bytes([KISS.CMD_SLOTTIME])+bytes([slottime])+bytes([KISS.FEND])
written = self.serial.write(kiss_command) written = self.serial.write(kiss_command)
if written != len(kiss_command): if written != len(kiss_command):
raise IOError("Could not configure KISS interface slot time to "+str(slottime_ms)+" (command value "+str(slottime)+")") raise OSError(f"Could not configure KISS interface slot time to {slottime_ms} (command value {slottime})")
def setFlowControl(self, flow_control): def setFlowControl(self, flow_control):
kiss_command = bytes([KISS.FEND])+bytes([KISS.CMD_READY])+bytes([0x01])+bytes([KISS.FEND]) kiss_command = bytes([KISS.FEND])+bytes([KISS.CMD_READY])+bytes([0x01])+bytes([KISS.FEND])
written = self.serial.write(kiss_command) written = self.serial.write(kiss_command)
if written != len(kiss_command): if written != len(kiss_command):
if (flow_control): if (flow_control):
raise IOError("Could not enable KISS interface flow control") raise OSError("Could not enable KISS interface flow control")
else: else:
raise IOError("Could not enable KISS interface flow control") raise OSError("Could not enable KISS interface flow control")
def processIncoming(self, data): def processIncoming(self, data):
@ -244,7 +244,7 @@ class KISSInterface(Interface):
self.first_tx = time.time() self.first_tx = time.time()
if written != len(frame): if written != len(frame):
raise IOError("Serial interface only wrote "+str(written)+" bytes of "+str(len(data))) raise OSError(f"Serial interface only wrote {written} bytes of {len(data)}")
else: else:
self.queue(data) self.queue(data)
@ -311,20 +311,20 @@ class KISSInterface(Interface):
if self.flow_control: if self.flow_control:
if not self.interface_ready: if not self.interface_ready:
if time.time() > self.flow_control_locked + self.flow_control_timeout: if time.time() > self.flow_control_locked + self.flow_control_timeout:
RNS.log("Interface "+str(self)+" is unlocking flow control due to time-out. This should not happen. Your hardware might have missed a flow-control READY command, or maybe it does not support flow-control.", RNS.LOG_WARNING) RNS.log(f"Interface {self} is unlocking flow control due to time-out. This should not happen. Your hardware might have missed a flow-control READY command, or maybe it does not support flow-control.", RNS.LOG_WARNING)
self.process_queue() self.process_queue()
if self.beacon_i != None and self.beacon_d != None: if self.beacon_i != None and self.beacon_d != None:
if self.first_tx != None: if self.first_tx != None:
if time.time() > self.first_tx + self.beacon_i: if time.time() > self.first_tx + self.beacon_i:
RNS.log("Interface "+str(self)+" is transmitting beacon data: "+str(self.beacon_d.decode("utf-8")), RNS.LOG_DEBUG) RNS.log(f"Interface {self} is transmitting beacon data: {self.beacon_d.decode('utf-8')}", RNS.LOG_DEBUG)
self.first_tx = None self.first_tx = None
self.processOutgoing(self.beacon_d) self.processOutgoing(self.beacon_d)
except Exception as e: except Exception as e:
self.online = False self.online = False
RNS.log("A serial port error occurred, the contained exception was: "+str(e), RNS.LOG_ERROR) RNS.log(f"A serial port error occurred, the contained exception was: {e}", RNS.LOG_ERROR)
RNS.log("The interface "+str(self)+" experienced an unrecoverable error and is now offline.", RNS.LOG_ERROR) RNS.log(f"The interface {self} experienced an unrecoverable error and is now offline.", RNS.LOG_ERROR)
if RNS.Reticulum.panic_on_interface_error: if RNS.Reticulum.panic_on_interface_error:
RNS.panic() RNS.panic()
@ -339,17 +339,17 @@ class KISSInterface(Interface):
while not self.online: while not self.online:
try: try:
time.sleep(5) time.sleep(5)
RNS.log("Attempting to reconnect serial port "+str(self.port)+" for "+str(self)+"...", RNS.LOG_VERBOSE) RNS.log(f"Attempting to reconnect serial port {self.port} for {self}...", RNS.LOG_VERBOSE)
self.open_port() self.open_port()
if self.serial.is_open: if self.serial.is_open:
self.configure_device() self.configure_device()
except Exception as e: except Exception as e:
RNS.log("Error while reconnecting port, the contained exception was: "+str(e), RNS.LOG_ERROR) RNS.log(f"Error while reconnecting port, the contained exception was: {e}", RNS.LOG_ERROR)
RNS.log("Reconnected serial port for "+str(self)) RNS.log(f"Reconnected serial port for {self}")
def should_ingress_limit(self): def should_ingress_limit(self):
return False return False
def __str__(self): def __str__(self):
return "KISSInterface["+self.name+"]" return f"KISSInterface[{self.name}]"

View File

@ -133,10 +133,10 @@ class LocalClientInterface(Interface):
self.connect() self.connect()
except Exception as e: except Exception as e:
RNS.log("Connection attempt for "+str(self)+" failed: "+str(e), RNS.LOG_DEBUG) RNS.log(f"Connection attempt for {self} failed: {e}", RNS.LOG_DEBUG)
if not self.never_connected: if not self.never_connected:
RNS.log("Reconnected socket for "+str(self)+".", RNS.LOG_INFO) RNS.log(f"Reconnected socket for {self}.", RNS.LOG_INFO)
self.reconnecting = False self.reconnecting = False
thread = threading.Thread(target=self.read_loop) thread = threading.Thread(target=self.read_loop)
@ -149,7 +149,7 @@ class LocalClientInterface(Interface):
else: else:
RNS.log("Attempt to reconnect on a non-initiator shared local interface. This should not happen.", RNS.LOG_ERROR) RNS.log("Attempt to reconnect on a non-initiator shared local interface. This should not happen.", RNS.LOG_ERROR)
raise IOError("Attempt to reconnect on a non-initiator local interface") raise OSError("Attempt to reconnect on a non-initiator local interface")
def processIncoming(self, data): def processIncoming(self, data):
@ -188,8 +188,8 @@ class LocalClientInterface(Interface):
self.parent_interface.txb += len(data) self.parent_interface.txb += len(data)
except Exception as e: except Exception as e:
RNS.log("Exception occurred while transmitting via "+str(self)+", tearing down interface", RNS.LOG_ERROR) RNS.log(f"Exception occurred while transmitting via {self}, tearing down interface", RNS.LOG_ERROR)
RNS.log("The contained exception was: "+str(e), RNS.LOG_ERROR) RNS.log(f"The contained exception was: {e}", RNS.LOG_ERROR)
self.teardown() self.teardown()
@ -226,7 +226,7 @@ class LocalClientInterface(Interface):
else: else:
self.online = False self.online = False
if self.is_connected_to_shared_instance and not self.detached: if self.is_connected_to_shared_instance and not self.detached:
RNS.log("Socket for "+str(self)+" was closed, attempting to reconnect...", RNS.LOG_WARNING) RNS.log(f"Socket for {self} was closed, attempting to reconnect...", RNS.LOG_WARNING)
RNS.Transport.shared_connection_disappeared() RNS.Transport.shared_connection_disappeared()
self.reconnect() self.reconnect()
else: else:
@ -237,26 +237,26 @@ class LocalClientInterface(Interface):
except Exception as e: except Exception as e:
self.online = False self.online = False
RNS.log("An interface error occurred, the contained exception was: "+str(e), RNS.LOG_ERROR) RNS.log(f"An interface error occurred, the contained exception was: {e}", RNS.LOG_ERROR)
RNS.log("Tearing down "+str(self), RNS.LOG_ERROR) RNS.log(f"Tearing down {self}", RNS.LOG_ERROR)
self.teardown() self.teardown()
def detach(self): def detach(self):
if self.socket != None: if self.socket != None:
if hasattr(self.socket, "close"): if hasattr(self.socket, "close"):
if callable(self.socket.close): if callable(self.socket.close):
RNS.log("Detaching "+str(self), RNS.LOG_DEBUG) RNS.log(f"Detaching {self}", RNS.LOG_DEBUG)
self.detached = True self.detached = True
try: try:
self.socket.shutdown(socket.SHUT_RDWR) self.socket.shutdown(socket.SHUT_RDWR)
except Exception as e: except Exception as e:
RNS.log("Error while shutting down socket for "+str(self)+": "+str(e)) RNS.log(f"Error while shutting down socket for {self}: {e}")
try: try:
self.socket.close() self.socket.close()
except Exception as e: except Exception as e:
RNS.log("Error while closing socket for "+str(self)+": "+str(e)) RNS.log(f"Error while closing socket for {self}: {e}")
self.socket = None self.socket = None
@ -276,7 +276,7 @@ class LocalClientInterface(Interface):
RNS.Transport.owner._should_persist_data() RNS.Transport.owner._should_persist_data()
if nowarning == False: if nowarning == False:
RNS.log("The interface "+str(self)+" experienced an unrecoverable error and is being torn down. Restart Reticulum to attempt to open this interface again.", RNS.LOG_ERROR) RNS.log(f"The interface {self} experienced an unrecoverable error and is being torn down. Restart Reticulum to attempt to open this interface again.", RNS.LOG_ERROR)
if RNS.Reticulum.panic_on_interface_error: if RNS.Reticulum.panic_on_interface_error:
RNS.panic() RNS.panic()
@ -288,7 +288,7 @@ class LocalClientInterface(Interface):
def __str__(self): def __str__(self):
return "LocalInterface["+str(self.target_port)+"]" return f"LocalInterface[{self.target_port}]"
class LocalServerInterface(Interface): class LocalServerInterface(Interface):
@ -360,7 +360,7 @@ class LocalServerInterface(Interface):
if from_spawned: self.oa_freq_deque.append(time.time()) if from_spawned: self.oa_freq_deque.append(time.time())
def __str__(self): def __str__(self):
return "Shared Instance["+str(self.bind_port)+"]" return f"Shared Instance[{self.bind_port}]"
class LocalInterfaceHandler(socketserver.BaseRequestHandler): class LocalInterfaceHandler(socketserver.BaseRequestHandler):
def __init__(self, callback, *args, **keys): def __init__(self, callback, *args, **keys):

View File

@ -72,17 +72,17 @@ class PipeInterface(Interface):
self.open_pipe() self.open_pipe()
except Exception as e: except Exception as e:
RNS.log("Could connect pipe for interface "+str(self), RNS.LOG_ERROR) RNS.log(f"Could connect pipe for interface {self}", RNS.LOG_ERROR)
raise e raise e
if self.pipe_is_open: if self.pipe_is_open:
self.configure_pipe() self.configure_pipe()
else: else:
raise IOError("Could not connect pipe") raise OSError("Could not connect pipe")
def open_pipe(self): def open_pipe(self):
RNS.log("Connecting subprocess pipe for "+str(self)+"...", RNS.LOG_VERBOSE) RNS.log(f"Connecting subprocess pipe for {self}...", RNS.LOG_VERBOSE)
try: try:
self.process = subprocess.Popen(shlex.split(self.command), stdin=subprocess.PIPE, stdout=subprocess.PIPE) self.process = subprocess.Popen(shlex.split(self.command), stdin=subprocess.PIPE, stdout=subprocess.PIPE)
@ -98,7 +98,7 @@ class PipeInterface(Interface):
thread.daemon = True thread.daemon = True
thread.start() thread.start()
self.online = True self.online = True
RNS.log("Subprocess pipe for "+str(self)+" is now connected", RNS.LOG_VERBOSE) RNS.log(f"Subprocess pipe for {self} is now connected", RNS.LOG_VERBOSE)
def processIncoming(self, data): def processIncoming(self, data):
@ -113,7 +113,7 @@ class PipeInterface(Interface):
self.process.stdin.flush() self.process.stdin.flush()
self.txb += len(data) self.txb += len(data)
if written != len(data): if written != len(data):
raise IOError("Pipe interface only wrote "+str(written)+" bytes of "+str(len(data))) raise OSError(f"Pipe interface only wrote {written} bytes of {len(data)}")
def readLoop(self): def readLoop(self):
@ -150,7 +150,7 @@ class PipeInterface(Interface):
escape = False escape = False
data_buffer = data_buffer+bytes([byte]) data_buffer = data_buffer+bytes([byte])
RNS.log("Subprocess terminated on "+str(self)) RNS.log(f"Subprocess terminated on {self}")
self.process.kill() self.process.kill()
except Exception as e: except Exception as e:
@ -160,8 +160,8 @@ class PipeInterface(Interface):
except Exception as e: except Exception as e:
pass pass
RNS.log("A pipe error occurred, the contained exception was: "+str(e), RNS.LOG_ERROR) RNS.log(f"A pipe error occurred, the contained exception was: {e}", RNS.LOG_ERROR)
RNS.log("The interface "+str(self)+" experienced an unrecoverable error and is now offline.", RNS.LOG_ERROR) RNS.log(f"The interface {self} experienced an unrecoverable error and is now offline.", RNS.LOG_ERROR)
if RNS.Reticulum.panic_on_interface_error: if RNS.Reticulum.panic_on_interface_error:
RNS.panic() RNS.panic()
@ -175,14 +175,14 @@ class PipeInterface(Interface):
while not self.online: while not self.online:
try: try:
time.sleep(self.respawn_delay) time.sleep(self.respawn_delay)
RNS.log("Attempting to respawn subprocess for "+str(self)+"...", RNS.LOG_VERBOSE) RNS.log(f"Attempting to respawn subprocess for {self}...", RNS.LOG_VERBOSE)
self.open_pipe() self.open_pipe()
if self.pipe_is_open: if self.pipe_is_open:
self.configure_pipe() self.configure_pipe()
except Exception as e: except Exception as e:
RNS.log("Error while spawning subprocess, the contained exception was: "+str(e), RNS.LOG_ERROR) RNS.log(f"Error while spawning subprocess, the contained exception was: {e}", RNS.LOG_ERROR)
RNS.log("Reconnected pipe for "+str(self)) RNS.log(f"Reconnected pipe for {self}")
def __str__(self): def __str__(self):
return "PipeInterface["+self.name+"]" return f"PipeInterface[{self.name}]"

View File

@ -211,31 +211,31 @@ class RNodeInterface(Interface):
self.validcfg = True self.validcfg = True
if (self.frequency < RNodeInterface.FREQ_MIN or self.frequency > RNodeInterface.FREQ_MAX): if (self.frequency < RNodeInterface.FREQ_MIN or self.frequency > RNodeInterface.FREQ_MAX):
RNS.log("Invalid frequency configured for "+str(self), RNS.LOG_ERROR) RNS.log(f"Invalid frequency configured for {self}", RNS.LOG_ERROR)
self.validcfg = False self.validcfg = False
if (self.txpower < 0 or self.txpower > 22): if (self.txpower < 0 or self.txpower > 22):
RNS.log("Invalid TX power configured for "+str(self), RNS.LOG_ERROR) RNS.log(f"Invalid TX power configured for {self}", RNS.LOG_ERROR)
self.validcfg = False self.validcfg = False
if (self.bandwidth < 7800 or self.bandwidth > 1625000): if (self.bandwidth < 7800 or self.bandwidth > 1625000):
RNS.log("Invalid bandwidth configured for "+str(self), RNS.LOG_ERROR) RNS.log(f"Invalid bandwidth configured for {self}", RNS.LOG_ERROR)
self.validcfg = False self.validcfg = False
if (self.sf < 5 or self.sf > 12): if (self.sf < 5 or self.sf > 12):
RNS.log("Invalid spreading factor configured for "+str(self), RNS.LOG_ERROR) RNS.log(f"Invalid spreading factor configured for {self}", RNS.LOG_ERROR)
self.validcfg = False self.validcfg = False
if (self.cr < 5 or self.cr > 8): if (self.cr < 5 or self.cr > 8):
RNS.log("Invalid coding rate configured for "+str(self), RNS.LOG_ERROR) RNS.log(f"Invalid coding rate configured for {self}", RNS.LOG_ERROR)
self.validcfg = False self.validcfg = False
if (self.st_alock and (self.st_alock < 0.0 or self.st_alock > 100.0)): if (self.st_alock and (self.st_alock < 0.0 or self.st_alock > 100.0)):
RNS.log("Invalid short-term airtime limit configured for "+str(self), RNS.LOG_ERROR) RNS.log(f"Invalid short-term airtime limit configured for {self}", RNS.LOG_ERROR)
self.validcfg = False self.validcfg = False
if (self.lt_alock and (self.lt_alock < 0.0 or self.lt_alock > 100.0)): if (self.lt_alock and (self.lt_alock < 0.0 or self.lt_alock > 100.0)):
RNS.log("Invalid long-term airtime limit configured for "+str(self), RNS.LOG_ERROR) RNS.log(f"Invalid long-term airtime limit configured for {self}", RNS.LOG_ERROR)
self.validcfg = False self.validcfg = False
if id_interval != None and id_callsign != None: if id_interval != None and id_callsign != None:
@ -244,14 +244,14 @@ class RNodeInterface(Interface):
self.id_callsign = id_callsign.encode("utf-8") self.id_callsign = id_callsign.encode("utf-8")
self.id_interval = id_interval self.id_interval = id_interval
else: else:
RNS.log("The encoded ID callsign for "+str(self)+" exceeds the max length of "+str(RNodeInterface.CALLSIGN_MAX_LEN)+" bytes.", RNS.LOG_ERROR) RNS.log(f"The encoded ID callsign for {self} exceeds the max length of {RNodeInterface.CALLSIGN_MAX_LEN} bytes.", RNS.LOG_ERROR)
self.validcfg = False self.validcfg = False
else: else:
self.id_interval = None self.id_interval = None
self.id_callsign = None self.id_callsign = None
if (not self.validcfg): if (not self.validcfg):
raise ValueError("The configuration for "+str(self)+" contains errors, interface is offline") raise ValueError(f"The configuration for {self} contains errors, interface is offline")
try: try:
self.open_port() self.open_port()
@ -259,11 +259,11 @@ class RNodeInterface(Interface):
if self.serial.is_open: if self.serial.is_open:
self.configure_device() self.configure_device()
else: else:
raise IOError("Could not open serial port") raise OSError("Could not open serial port")
except Exception as e: except Exception as e:
RNS.log("Could not open serial port for interface "+str(self), RNS.LOG_ERROR) RNS.log(f"Could not open serial port for interface {self}", RNS.LOG_ERROR)
RNS.log("The contained exception was: "+str(e), RNS.LOG_ERROR) RNS.log(f"The contained exception was: {e}", RNS.LOG_ERROR)
RNS.log("Reticulum will attempt to bring up this interface periodically", RNS.LOG_ERROR) RNS.log("Reticulum will attempt to bring up this interface periodically", RNS.LOG_ERROR)
if not self.detached and not self.reconnecting: if not self.detached and not self.reconnecting:
thread = threading.Thread(target=self.reconnect_port) thread = threading.Thread(target=self.reconnect_port)
@ -273,7 +273,7 @@ class RNodeInterface(Interface):
def open_port(self): def open_port(self):
if not self.use_ble: if not self.use_ble:
RNS.log("Opening serial port "+self.port+"...") RNS.log(f"Opening serial port {self.port}...")
self.serial = self.pyserial.Serial( self.serial = self.pyserial.Serial(
port = self.port, port = self.port,
baudrate = self.speed, baudrate = self.speed,
@ -290,6 +290,11 @@ class RNodeInterface(Interface):
else: else:
RNS.log(f"Opening BLE connection for {self}...") RNS.log(f"Opening BLE connection for {self}...")
if self.ble != None and self.ble.running == False:
self.ble.close()
self.ble.cleanup()
self.ble = None
if self.ble == None: if self.ble == None:
self.ble = BLEConnection(owner=self, target_name=self.ble_name, target_bt_addr=self.ble_addr) self.ble = BLEConnection(owner=self, target_name=self.ble_name, target_bt_addr=self.ble_addr)
self.serial = self.ble self.serial = self.ble
@ -298,8 +303,7 @@ class RNodeInterface(Interface):
while not self.ble.connected and time.time() < open_time + self.ble.CONNECT_TIMEOUT: while not self.ble.connected and time.time() < open_time + self.ble.CONNECT_TIMEOUT:
time.sleep(1) time.sleep(1)
def reset_radio_state(self):
def configure_device(self):
self.r_frequency = None self.r_frequency = None
self.r_bandwidth = None self.r_bandwidth = None
self.r_txpower = None self.r_txpower = None
@ -307,6 +311,10 @@ class RNodeInterface(Interface):
self.r_cr = None self.r_cr = None
self.r_state = None self.r_state = None
self.r_lock = None self.r_lock = None
self.detected = False
def configure_device(self):
self.reset_radio_state()
sleep(2.0) sleep(2.0)
thread = threading.Thread(target=self.readLoop) thread = threading.Thread(target=self.readLoop)
@ -327,22 +335,22 @@ class RNodeInterface(Interface):
RNS.log(f"RNode detect timed out over {self.port}", RNS.LOG_ERROR) RNS.log(f"RNode detect timed out over {self.port}", RNS.LOG_ERROR)
if not self.detected: if not self.detected:
RNS.log("Could not detect device for "+str(self), RNS.LOG_ERROR) RNS.log(f"Could not detect device for {self}", RNS.LOG_ERROR)
self.serial.close() self.serial.close()
else: else:
if self.platform == KISS.PLATFORM_ESP32 or self.platform == KISS.PLATFORM_NRF52: if self.platform == KISS.PLATFORM_ESP32 or self.platform == KISS.PLATFORM_NRF52:
self.display = True self.display = True
RNS.log("Serial port "+self.port+" is now open") RNS.log(f"Serial port {self.port} is now open")
RNS.log("Configuring RNode interface...", RNS.LOG_VERBOSE) RNS.log("Configuring RNode interface...", RNS.LOG_VERBOSE)
self.initRadio() self.initRadio()
if (self.validateRadioState()): if (self.validateRadioState()):
self.interface_ready = True self.interface_ready = True
RNS.log(str(self)+" is configured and powered up") RNS.log(f"{self} is configured and powered up")
sleep(0.3) sleep(0.3)
self.online = True self.online = True
else: else:
RNS.log("After configuring "+str(self)+", the reported radio parameters did not match your configuration.", RNS.LOG_ERROR) RNS.log(f"After configuring {self}, the reported radio parameters did not match your configuration.", RNS.LOG_ERROR)
RNS.log("Make sure that your hardware actually supports the parameters specified in the configuration", RNS.LOG_ERROR) RNS.log("Make sure that your hardware actually supports the parameters specified in the configuration", RNS.LOG_ERROR)
RNS.log("Aborting RNode startup", RNS.LOG_ERROR) RNS.log("Aborting RNode startup", RNS.LOG_ERROR)
self.serial.close() self.serial.close()
@ -365,27 +373,27 @@ class RNodeInterface(Interface):
kiss_command = bytes([KISS.FEND, KISS.CMD_DETECT, KISS.DETECT_REQ, KISS.FEND, KISS.CMD_FW_VERSION, 0x00, KISS.FEND, KISS.CMD_PLATFORM, 0x00, KISS.FEND, KISS.CMD_MCU, 0x00, KISS.FEND]) kiss_command = bytes([KISS.FEND, KISS.CMD_DETECT, KISS.DETECT_REQ, KISS.FEND, KISS.CMD_FW_VERSION, 0x00, KISS.FEND, KISS.CMD_PLATFORM, 0x00, KISS.FEND, KISS.CMD_MCU, 0x00, KISS.FEND])
written = self.serial.write(kiss_command) written = self.serial.write(kiss_command)
if written != len(kiss_command): if written != len(kiss_command):
raise IOError("An IO error occurred while detecting hardware for "+str(self)) raise OSError(f"An IO error occurred while detecting hardware for {self}")
def leave(self): def leave(self):
kiss_command = bytes([KISS.FEND, KISS.CMD_LEAVE, 0xFF, KISS.FEND]) kiss_command = bytes([KISS.FEND, KISS.CMD_LEAVE, 0xFF, KISS.FEND])
written = self.serial.write(kiss_command) written = self.serial.write(kiss_command)
if written != len(kiss_command): if written != len(kiss_command):
raise IOError("An IO error occurred while sending host left command to device") raise OSError("An IO error occurred while sending host left command to device")
def enable_external_framebuffer(self): def enable_external_framebuffer(self):
if self.display != None: if self.display != None:
kiss_command = bytes([KISS.FEND, KISS.CMD_FB_EXT, 0x01, KISS.FEND]) kiss_command = bytes([KISS.FEND, KISS.CMD_FB_EXT, 0x01, KISS.FEND])
written = self.serial.write(kiss_command) written = self.serial.write(kiss_command)
if written != len(kiss_command): if written != len(kiss_command):
raise IOError("An IO error occurred while enabling external framebuffer on device") raise OSError("An IO error occurred while enabling external framebuffer on device")
def disable_external_framebuffer(self): def disable_external_framebuffer(self):
if self.display != None: if self.display != None:
kiss_command = bytes([KISS.FEND, KISS.CMD_FB_EXT, 0x00, KISS.FEND]) kiss_command = bytes([KISS.FEND, KISS.CMD_FB_EXT, 0x00, KISS.FEND])
written = self.serial.write(kiss_command) written = self.serial.write(kiss_command)
if written != len(kiss_command): if written != len(kiss_command):
raise IOError("An IO error occurred while disabling external framebuffer on device") raise OSError("An IO error occurred while disabling external framebuffer on device")
FB_PIXEL_WIDTH = 64 FB_PIXEL_WIDTH = 64
FB_BITS_PER_PIXEL = 1 FB_BITS_PER_PIXEL = 1
@ -409,13 +417,13 @@ class RNodeInterface(Interface):
written = self.serial.write(kiss_command) written = self.serial.write(kiss_command)
if written != len(kiss_command): if written != len(kiss_command):
raise IOError("An IO error occurred while writing framebuffer data device") raise OSError("An IO error occurred while writing framebuffer data device")
def hard_reset(self): def hard_reset(self):
kiss_command = bytes([KISS.FEND, KISS.CMD_RESET, 0xf8, KISS.FEND]) kiss_command = bytes([KISS.FEND, KISS.CMD_RESET, 0xf8, KISS.FEND])
written = self.serial.write(kiss_command) written = self.serial.write(kiss_command)
if written != len(kiss_command): if written != len(kiss_command):
raise IOError("An IO error occurred while restarting device") raise OSError("An IO error occurred while restarting device")
sleep(2.25); sleep(2.25);
def setFrequency(self): def setFrequency(self):
@ -428,7 +436,7 @@ class RNodeInterface(Interface):
kiss_command = bytes([KISS.FEND])+bytes([KISS.CMD_FREQUENCY])+data+bytes([KISS.FEND]) kiss_command = bytes([KISS.FEND])+bytes([KISS.CMD_FREQUENCY])+data+bytes([KISS.FEND])
written = self.serial.write(kiss_command) written = self.serial.write(kiss_command)
if written != len(kiss_command): if written != len(kiss_command):
raise IOError("An IO error occurred while configuring frequency for "+str(self)) raise OSError(f"An IO error occurred while configuring frequency for {self}")
def setBandwidth(self): def setBandwidth(self):
c1 = self.bandwidth >> 24 c1 = self.bandwidth >> 24
@ -440,28 +448,28 @@ class RNodeInterface(Interface):
kiss_command = bytes([KISS.FEND])+bytes([KISS.CMD_BANDWIDTH])+data+bytes([KISS.FEND]) kiss_command = bytes([KISS.FEND])+bytes([KISS.CMD_BANDWIDTH])+data+bytes([KISS.FEND])
written = self.serial.write(kiss_command) written = self.serial.write(kiss_command)
if written != len(kiss_command): if written != len(kiss_command):
raise IOError("An IO error occurred while configuring bandwidth for "+str(self)) raise OSError(f"An IO error occurred while configuring bandwidth for {self}")
def setTXPower(self): def setTXPower(self):
txp = bytes([self.txpower]) txp = bytes([self.txpower])
kiss_command = bytes([KISS.FEND])+bytes([KISS.CMD_TXPOWER])+txp+bytes([KISS.FEND]) kiss_command = bytes([KISS.FEND])+bytes([KISS.CMD_TXPOWER])+txp+bytes([KISS.FEND])
written = self.serial.write(kiss_command) written = self.serial.write(kiss_command)
if written != len(kiss_command): if written != len(kiss_command):
raise IOError("An IO error occurred while configuring TX power for "+str(self)) raise OSError(f"An IO error occurred while configuring TX power for {self}")
def setSpreadingFactor(self): def setSpreadingFactor(self):
sf = bytes([self.sf]) sf = bytes([self.sf])
kiss_command = bytes([KISS.FEND])+bytes([KISS.CMD_SF])+sf+bytes([KISS.FEND]) kiss_command = bytes([KISS.FEND])+bytes([KISS.CMD_SF])+sf+bytes([KISS.FEND])
written = self.serial.write(kiss_command) written = self.serial.write(kiss_command)
if written != len(kiss_command): if written != len(kiss_command):
raise IOError("An IO error occurred while configuring spreading factor for "+str(self)) raise OSError(f"An IO error occurred while configuring spreading factor for {self}")
def setCodingRate(self): def setCodingRate(self):
cr = bytes([self.cr]) cr = bytes([self.cr])
kiss_command = bytes([KISS.FEND])+bytes([KISS.CMD_CR])+cr+bytes([KISS.FEND]) kiss_command = bytes([KISS.FEND])+bytes([KISS.CMD_CR])+cr+bytes([KISS.FEND])
written = self.serial.write(kiss_command) written = self.serial.write(kiss_command)
if written != len(kiss_command): if written != len(kiss_command):
raise IOError("An IO error occurred while configuring coding rate for "+str(self)) raise OSError(f"An IO error occurred while configuring coding rate for {self}")
def setSTALock(self): def setSTALock(self):
if self.st_alock != None: if self.st_alock != None:
@ -473,7 +481,7 @@ class RNodeInterface(Interface):
kiss_command = bytes([KISS.FEND])+bytes([KISS.CMD_ST_ALOCK])+data+bytes([KISS.FEND]) kiss_command = bytes([KISS.FEND])+bytes([KISS.CMD_ST_ALOCK])+data+bytes([KISS.FEND])
written = self.serial.write(kiss_command) written = self.serial.write(kiss_command)
if written != len(kiss_command): if written != len(kiss_command):
raise IOError("An IO error occurred while configuring short-term airtime limit for "+str(self)) raise OSError(f"An IO error occurred while configuring short-term airtime limit for {self}")
def setLTALock(self): def setLTALock(self):
if self.lt_alock != None: if self.lt_alock != None:
@ -485,14 +493,14 @@ class RNodeInterface(Interface):
kiss_command = bytes([KISS.FEND])+bytes([KISS.CMD_LT_ALOCK])+data+bytes([KISS.FEND]) kiss_command = bytes([KISS.FEND])+bytes([KISS.CMD_LT_ALOCK])+data+bytes([KISS.FEND])
written = self.serial.write(kiss_command) written = self.serial.write(kiss_command)
if written != len(kiss_command): if written != len(kiss_command):
raise IOError("An IO error occurred while configuring long-term airtime limit for "+str(self)) raise OSError(f"An IO error occurred while configuring long-term airtime limit for {self}")
def setRadioState(self, state): def setRadioState(self, state):
self.state = state self.state = state
kiss_command = bytes([KISS.FEND])+bytes([KISS.CMD_RADIO_STATE])+bytes([state])+bytes([KISS.FEND]) kiss_command = bytes([KISS.FEND])+bytes([KISS.CMD_RADIO_STATE])+bytes([state])+bytes([KISS.FEND])
written = self.serial.write(kiss_command) written = self.serial.write(kiss_command)
if written != len(kiss_command): if written != len(kiss_command):
raise IOError("An IO error occurred while configuring radio state for "+str(self)) raise OSError(f"An IO error occurred while configuring radio state for {self}")
def validate_firmware(self): def validate_firmware(self):
if (self.maj_version > RNodeInterface.REQUIRED_FW_VER_MAJ): if (self.maj_version > RNodeInterface.REQUIRED_FW_VER_MAJ):
@ -505,15 +513,22 @@ class RNodeInterface(Interface):
if self.firmware_ok: if self.firmware_ok:
return return
RNS.log("The firmware version of the connected RNode is "+str(self.maj_version)+"."+str(self.min_version), RNS.LOG_ERROR) RNS.log(f"The firmware version of the connected RNode is {self.maj_version}.{self.min_version}", RNS.LOG_ERROR)
RNS.log("This version of Reticulum requires at least version "+str(RNodeInterface.REQUIRED_FW_VER_MAJ)+"."+str(RNodeInterface.REQUIRED_FW_VER_MIN), RNS.LOG_ERROR) RNS.log(f"This version of Reticulum requires at least version {RNodeInterface.REQUIRED_FW_VER_MAJ}.{RNodeInterface.REQUIRED_FW_VER_MIN}", RNS.LOG_ERROR)
RNS.log("Please update your RNode firmware with rnodeconf from https://github.com/markqvist/rnodeconfigutil/") RNS.log("Please update your RNode firmware with rnodeconf from https://github.com/markqvist/rnodeconfigutil/")
RNS.panic() RNS.panic()
def validateRadioState(self): def validateRadioState(self):
RNS.log("Waiting for radio configuration validation for "+str(self)+"...", RNS.LOG_VERBOSE) RNS.log(f"Waiting for radio configuration validation for {self}...", RNS.LOG_VERBOSE)
sleep(0.25); if self.use_ble:
sleep(1.00)
else:
sleep(0.25)
if self.use_ble and self.ble != None and self.ble.device_disappeared:
RNS.log(f"Device disappeared during radio state validation for {self}", RNS.LOG_ERROR)
return False
self.validcfg = True self.validcfg = True
if (self.r_frequency != None and abs(self.frequency - int(self.r_frequency)) > 100): if (self.r_frequency != None and abs(self.frequency - int(self.r_frequency)) > 100):
@ -542,7 +557,7 @@ class RNodeInterface(Interface):
try: try:
self.bitrate = self.r_sf * ( (4.0/self.r_cr) / (math.pow(2,self.r_sf)/(self.r_bandwidth/1000)) ) * 1000 self.bitrate = self.r_sf * ( (4.0/self.r_cr) / (math.pow(2,self.r_sf)/(self.r_bandwidth/1000)) ) * 1000
self.bitrate_kbps = round(self.bitrate/1000.0, 2) self.bitrate_kbps = round(self.bitrate/1000.0, 2)
RNS.log(str(self)+" On-air bitrate is now "+str(self.bitrate_kbps)+ " kbps", RNS.LOG_VERBOSE) RNS.log(f"{self} On-air bitrate is now {self.bitrate_kbps} kbps", RNS.LOG_VERBOSE)
except: except:
self.bitrate = 0 self.bitrate = 0
@ -573,7 +588,7 @@ class RNodeInterface(Interface):
self.txb += datalen self.txb += datalen
if written != len(frame): if written != len(frame):
raise IOError("Serial interface only wrote "+str(written)+" bytes of "+str(len(data))) raise OSError(f"Serial interface only wrote {written} bytes of {len(data)}")
else: else:
self.queue(data) self.queue(data)
@ -639,7 +654,7 @@ class RNodeInterface(Interface):
command_buffer = command_buffer+bytes([byte]) command_buffer = command_buffer+bytes([byte])
if (len(command_buffer) == 4): if (len(command_buffer) == 4):
self.r_frequency = command_buffer[0] << 24 | command_buffer[1] << 16 | command_buffer[2] << 8 | command_buffer[3] self.r_frequency = command_buffer[0] << 24 | command_buffer[1] << 16 | command_buffer[2] << 8 | command_buffer[3]
RNS.log(str(self)+" Radio reporting frequency is "+str(self.r_frequency/1000000.0)+" MHz", RNS.LOG_DEBUG) RNS.log(f"{self} Radio reporting frequency is {self.r_frequency / 1000000.0} MHz", RNS.LOG_DEBUG)
self.updateBitrate() self.updateBitrate()
elif (command == KISS.CMD_BANDWIDTH): elif (command == KISS.CMD_BANDWIDTH):
@ -655,26 +670,26 @@ class RNodeInterface(Interface):
command_buffer = command_buffer+bytes([byte]) command_buffer = command_buffer+bytes([byte])
if (len(command_buffer) == 4): if (len(command_buffer) == 4):
self.r_bandwidth = command_buffer[0] << 24 | command_buffer[1] << 16 | command_buffer[2] << 8 | command_buffer[3] self.r_bandwidth = command_buffer[0] << 24 | command_buffer[1] << 16 | command_buffer[2] << 8 | command_buffer[3]
RNS.log(str(self)+" Radio reporting bandwidth is "+str(self.r_bandwidth/1000.0)+" KHz", RNS.LOG_DEBUG) RNS.log(f"{self} Radio reporting bandwidth is {self.r_bandwidth / 1000.0} KHz", RNS.LOG_DEBUG)
self.updateBitrate() self.updateBitrate()
elif (command == KISS.CMD_TXPOWER): elif (command == KISS.CMD_TXPOWER):
self.r_txpower = byte self.r_txpower = byte
RNS.log(str(self)+" Radio reporting TX power is "+str(self.r_txpower)+" dBm", RNS.LOG_DEBUG) RNS.log(f"{self} Radio reporting TX power is {self.r_txpower} dBm", RNS.LOG_DEBUG)
elif (command == KISS.CMD_SF): elif (command == KISS.CMD_SF):
self.r_sf = byte self.r_sf = byte
RNS.log(str(self)+" Radio reporting spreading factor is "+str(self.r_sf), RNS.LOG_DEBUG) RNS.log(f"{self} Radio reporting spreading factor is {self.r_sf}", RNS.LOG_DEBUG)
self.updateBitrate() self.updateBitrate()
elif (command == KISS.CMD_CR): elif (command == KISS.CMD_CR):
self.r_cr = byte self.r_cr = byte
RNS.log(str(self)+" Radio reporting coding rate is "+str(self.r_cr), RNS.LOG_DEBUG) RNS.log(f"{self} Radio reporting coding rate is {self.r_cr}", RNS.LOG_DEBUG)
self.updateBitrate() self.updateBitrate()
elif (command == KISS.CMD_RADIO_STATE): elif (command == KISS.CMD_RADIO_STATE):
self.r_state = byte self.r_state = byte
if self.r_state: if self.r_state:
pass pass
else: else:
RNS.log(str(self)+" Radio reporting state is offline", RNS.LOG_DEBUG) RNS.log(f"{self} Radio reporting state is offline", RNS.LOG_DEBUG)
elif (command == KISS.CMD_RADIO_LOCK): elif (command == KISS.CMD_RADIO_LOCK):
self.r_lock = byte self.r_lock = byte
@ -752,7 +767,7 @@ class RNodeInterface(Interface):
if (len(command_buffer) == 2): if (len(command_buffer) == 2):
at = command_buffer[0] << 8 | command_buffer[1] at = command_buffer[0] << 8 | command_buffer[1]
self.r_st_alock = at/100.0 self.r_st_alock = at/100.0
RNS.log(str(self)+" Radio reporting short-term airtime limit is "+str(self.r_st_alock)+"%", RNS.LOG_DEBUG) RNS.log(f"{self} Radio reporting short-term airtime limit is {self.r_st_alock}%", RNS.LOG_DEBUG)
elif (command == KISS.CMD_LT_ALOCK): elif (command == KISS.CMD_LT_ALOCK):
if (byte == KISS.FESC): if (byte == KISS.FESC):
escape = True escape = True
@ -767,7 +782,7 @@ class RNodeInterface(Interface):
if (len(command_buffer) == 2): if (len(command_buffer) == 2):
at = command_buffer[0] << 8 | command_buffer[1] at = command_buffer[0] << 8 | command_buffer[1]
self.r_lt_alock = at/100.0 self.r_lt_alock = at/100.0
RNS.log(str(self)+" Radio reporting long-term airtime limit is "+str(self.r_lt_alock)+"%", RNS.LOG_DEBUG) RNS.log(f"{self} Radio reporting long-term airtime limit is {self.r_lt_alock}%", RNS.LOG_DEBUG)
elif (command == KISS.CMD_STAT_CHTM): elif (command == KISS.CMD_STAT_CHTM):
if (byte == KISS.FESC): if (byte == KISS.FESC):
escape = True escape = True
@ -813,9 +828,9 @@ class RNodeInterface(Interface):
self.r_preamble_symbols = prs self.r_preamble_symbols = prs
self.r_premable_time_ms = prt self.r_premable_time_ms = prt
self.r_csma_slot_time_ms = cst self.r_csma_slot_time_ms = cst
RNS.log(str(self)+" Radio reporting symbol time is "+str(round(self.r_symbol_time_ms,2))+"ms (at "+str(self.r_symbol_rate)+" baud)", RNS.LOG_DEBUG) RNS.log(f"{self} Radio reporting symbol time is {round(self.r_symbol_time_ms, 2)}ms (at {self.r_symbol_rate} baud)", RNS.LOG_DEBUG)
RNS.log(str(self)+" Radio reporting preamble is "+str(self.r_preamble_symbols)+" symbols ("+str(self.r_premable_time_ms)+"ms)", RNS.LOG_DEBUG) RNS.log(f"{self} Radio reporting preamble is {self.r_preamble_symbols} symbols ({self.r_premable_time_ms}ms)", RNS.LOG_DEBUG)
RNS.log(str(self)+" Radio reporting CSMA slot time is "+str(self.r_csma_slot_time_ms)+"ms", RNS.LOG_DEBUG) RNS.log(f"{self} Radio reporting CSMA slot time is {self.r_csma_slot_time_ms}ms", RNS.LOG_DEBUG)
elif (command == KISS.CMD_STAT_BAT): elif (command == KISS.CMD_STAT_BAT):
if (byte == KISS.FESC): if (byte == KISS.FESC):
escape = True escape = True
@ -843,26 +858,26 @@ class RNodeInterface(Interface):
self.mcu = byte self.mcu = byte
elif (command == KISS.CMD_ERROR): elif (command == KISS.CMD_ERROR):
if (byte == KISS.ERROR_INITRADIO): if (byte == KISS.ERROR_INITRADIO):
RNS.log(str(self)+" hardware initialisation error (code "+RNS.hexrep(byte)+")", RNS.LOG_ERROR) RNS.log(f"{self} hardware initialisation error (code {RNS.hexrep(byte)})", RNS.LOG_ERROR)
raise IOError("Radio initialisation failure") raise OSError("Radio initialisation failure")
elif (byte == KISS.ERROR_TXFAILED): elif (byte == KISS.ERROR_TXFAILED):
RNS.log(str(self)+" hardware TX error (code "+RNS.hexrep(byte)+")", RNS.LOG_ERROR) RNS.log(f"{self} hardware TX error (code {RNS.hexrep(byte)})", RNS.LOG_ERROR)
raise IOError("Hardware transmit failure") raise OSError("Hardware transmit failure")
elif (byte == KISS.ERROR_MEMORY_LOW): elif (byte == KISS.ERROR_MEMORY_LOW):
RNS.log(str(self)+" hardware error (code "+RNS.hexrep(byte)+"): Memory exhausted", RNS.LOG_ERROR) RNS.log(f"{self} hardware error (code {RNS.hexrep(byte)}): Memory exhausted", RNS.LOG_ERROR)
self.hw_errors.append({"error": KISS.ERROR_MEMORY_LOW, "description": "Memory exhausted on connected device"}) self.hw_errors.append({"error": KISS.ERROR_MEMORY_LOW, "description": "Memory exhausted on connected device"})
elif (byte == KISS.ERROR_MODEM_TIMEOUT): elif (byte == KISS.ERROR_MODEM_TIMEOUT):
RNS.log(str(self)+" hardware error (code "+RNS.hexrep(byte)+"): Modem communication timed out", RNS.LOG_ERROR) RNS.log(f"{self} hardware error (code {RNS.hexrep(byte)}): Modem communication timed out", RNS.LOG_ERROR)
self.hw_errors.append({"error": KISS.ERROR_MODEM_TIMEOUT, "description": "Modem communication timed out on connected device"}) self.hw_errors.append({"error": KISS.ERROR_MODEM_TIMEOUT, "description": "Modem communication timed out on connected device"})
else: else:
RNS.log(str(self)+" hardware error (code "+RNS.hexrep(byte)+")", RNS.LOG_ERROR) RNS.log(f"{self} hardware error (code {RNS.hexrep(byte)})", RNS.LOG_ERROR)
raise IOError("Unknown hardware failure") raise OSError("Unknown hardware failure")
elif (command == KISS.CMD_RESET): elif (command == KISS.CMD_RESET):
if (byte == 0xF8): if (byte == 0xF8):
if self.platform == KISS.PLATFORM_ESP32: if self.platform == KISS.PLATFORM_ESP32:
if self.online: if self.online:
RNS.log("Detected reset while device was online, reinitialising device...", RNS.LOG_ERROR) RNS.log("Detected reset while device was online, reinitialising device...", RNS.LOG_ERROR)
raise IOError("ESP32 reset") raise OSError("ESP32 reset")
elif (command == KISS.CMD_READY): elif (command == KISS.CMD_READY):
self.process_queue() self.process_queue()
elif (command == KISS.CMD_DETECT): elif (command == KISS.CMD_DETECT):
@ -874,7 +889,7 @@ class RNodeInterface(Interface):
else: else:
time_since_last = int(time.time()*1000) - last_read_ms time_since_last = int(time.time()*1000) - last_read_ms
if len(data_buffer) > 0 and time_since_last > self.timeout: if len(data_buffer) > 0 and time_since_last > self.timeout:
RNS.log(str(self)+" serial read timeout in command "+str(command), RNS.LOG_WARNING) RNS.log(f"{self} serial read timeout in command {command}", RNS.LOG_WARNING)
data_buffer = b"" data_buffer = b""
in_frame = False in_frame = False
command = KISS.CMD_UNKNOWN command = KISS.CMD_UNKNOWN
@ -883,15 +898,15 @@ class RNodeInterface(Interface):
if self.id_interval != None and self.id_callsign != None: if self.id_interval != None and self.id_callsign != None:
if self.first_tx != None: if self.first_tx != None:
if time.time() > self.first_tx + self.id_interval: if time.time() > self.first_tx + self.id_interval:
RNS.log("Interface "+str(self)+" is transmitting beacon data: "+str(self.id_callsign.decode("utf-8")), RNS.LOG_DEBUG) RNS.log(f"Interface {self} is transmitting beacon data: {self.id_callsign.decode('utf-8')}", RNS.LOG_DEBUG)
self.processOutgoing(self.id_callsign) self.processOutgoing(self.id_callsign)
sleep(0.08) sleep(0.08)
except Exception as e: except Exception as e:
self.online = False self.online = False
RNS.log("A serial port error occurred, the contained exception was: "+str(e), RNS.LOG_ERROR) RNS.log(f"A serial port error occurred, the contained exception was: {e}", RNS.LOG_ERROR)
RNS.log("The interface "+str(self)+" experienced an unrecoverable error and is now offline.", RNS.LOG_ERROR) RNS.log(f"The interface {self} experienced an unrecoverable error and is now offline.", RNS.LOG_ERROR)
if RNS.Reticulum.panic_on_interface_error: if RNS.Reticulum.panic_on_interface_error:
RNS.panic() RNS.panic()
@ -912,16 +927,16 @@ class RNodeInterface(Interface):
while not self.online and not self.detached: while not self.online and not self.detached:
try: try:
time.sleep(5) time.sleep(5)
RNS.log("Attempting to reconnect serial port "+str(self.port)+" for "+str(self)+"...", RNS.LOG_VERBOSE) RNS.log(f"Attempting to reconnect serial port {self.port} for {self}...", RNS.LOG_VERBOSE)
self.open_port() self.open_port()
if self.serial.is_open: if self.serial.is_open:
self.configure_device() self.configure_device()
except Exception as e: except Exception as e:
RNS.log("Error while reconnecting port, the contained exception was: "+str(e), RNS.LOG_ERROR) RNS.log(f"Error while reconnecting port, the contained exception was: {e}", RNS.LOG_ERROR)
self.reconnecting = False self.reconnecting = False
if self.online: if self.online:
RNS.log("Reconnected serial port for "+str(self)) RNS.log(f"Reconnected serial port for {self}")
def detach(self): def detach(self):
self.detached = True self.detached = True
@ -965,7 +980,7 @@ class RNodeInterface(Interface):
return data return data
def __str__(self): def __str__(self):
return "RNodeInterface["+str(self.name)+"]" return f"RNodeInterface[{self.name}]"
class BLEConnection(): class BLEConnection():
UART_SERVICE_UUID = "6E400001-B5A3-F393-E0A9-E50E24DCCA9E" UART_SERVICE_UUID = "6E400001-B5A3-F393-E0A9-E50E24DCCA9E"
@ -1010,11 +1025,13 @@ class BLEConnection():
self.target_bt_addr = target_bt_addr self.target_bt_addr = target_bt_addr
self.scan_timeout = BLEConnection.SCAN_TIMEOUT self.scan_timeout = BLEConnection.SCAN_TIMEOUT
self.ble_device = None self.ble_device = None
self.last_client = None
self.connected = False self.connected = False
self.running = False self.running = False
self.should_run = False self.should_run = False
self.must_disconnect = False self.must_disconnect = False
self.connect_job_running = False self.connect_job_running = False
self.device_disappeared = False
import importlib import importlib
if BLEConnection.bleak == None: if BLEConnection.bleak == None:
@ -1032,8 +1049,17 @@ class BLEConnection():
self.should_run = True self.should_run = True
self.connection_thread = threading.Thread(target=self.connection_job, daemon=True).start() self.connection_thread = threading.Thread(target=self.connection_job, daemon=True).start()
def cleanup(self):
try:
if self.last_client != None:
self.asyncio.run(self.last_client.disconnect())
except Exception as e:
RNS.log(f"Error while disconnecting BLE device on cleanup for {self.owner}", RNS.LOG_ERROR)
self.should_run = False
def connection_job(self): def connection_job(self):
while (self.should_run): while self.should_run:
if self.ble_device == None: if self.ble_device == None:
self.ble_device = self.find_target_device() self.ble_device = self.find_target_device()
@ -1043,6 +1069,10 @@ class BLEConnection():
time.sleep(1) time.sleep(1)
self.cleanup()
self.running = False
RNS.log(f"BLE connection job for {self.owner} ended", RNS.LOG_DEBUG)
def connect_device(self): def connect_device(self):
if self.ble_device != None and type(self.ble_device) == self.bleak.backends.device.BLEDevice: if self.ble_device != None and type(self.ble_device) == self.bleak.backends.device.BLEDevice:
RNS.log(f"Connecting BLE device {self.ble_device} for {self.owner}...", RNS.LOG_DEBUG) RNS.log(f"Connecting BLE device {self.ble_device} for {self.owner}...", RNS.LOG_DEBUG)
@ -1056,6 +1086,7 @@ class BLEConnection():
self.connected = True self.connected = True
self.ble_device = ble_client self.ble_device = ble_client
self.last_client = ble_client
self.owner.port = str(f"ble://{ble_client.address}") self.owner.port = str(f"ble://{ble_client.address}")
loop = self.asyncio.get_running_loop() loop = self.asyncio.get_running_loop()
@ -1077,12 +1108,14 @@ class BLEConnection():
self.asyncio.run(connect_job()) self.asyncio.run(connect_job())
except Exception as e: except Exception as e:
RNS.log(f"Could not connect BLE device {self.ble_device} for {self.owner}. Possibly missing authentication.", RNS.LOG_ERROR) RNS.log(f"Could not connect BLE device {self.ble_device} for {self.owner}. Possibly missing authentication.", RNS.LOG_ERROR)
self.connect_job_running = False self.connect_job_running = False
def device_disconnected(self, device): def device_disconnected(self, device):
RNS.log(f"BLE device for {self.owner} disconnected", RNS.LOG_NOTICE) RNS.log(f"BLE device for {self.owner} disconnected", RNS.LOG_NOTICE)
self.connected = False self.connected = False
self.ble_device = None self.ble_device = None
self.device_disappeared = True
def find_target_device(self): def find_target_device(self):
RNS.log(f"Searching for attachable BLE device for {self.owner}...", RNS.LOG_EXTREME) RNS.log(f"Searching for attachable BLE device for {self.owner}...", RNS.LOG_EXTREME)
@ -1106,7 +1139,13 @@ class BLEConnection():
return False return False
device = self.asyncio.run(self.bleak.BleakScanner.find_device_by_filter(device_filter, timeout=self.scan_timeout)) device = None
try:
device = self.asyncio.run(self.bleak.BleakScanner.find_device_by_filter(device_filter, timeout=self.scan_timeout))
except Exception as e:
RNS.log(f"Error while finding BLE device for {self.owner}: {e}", RNS.LOG_ERROR)
self.should_run = False
return device return device
def device_bonded(self, device): def device_bonded(self, device):

View File

@ -241,14 +241,14 @@ class RNodeMultiInterface(Interface):
self.id_callsign = id_callsign.encode("utf-8") self.id_callsign = id_callsign.encode("utf-8")
self.id_interval = id_interval self.id_interval = id_interval
else: else:
RNS.log("The encoded ID callsign for "+str(self)+" exceeds the max length of "+str(RNodeMultiInterface.CALLSIGN_MAX_LEN)+" bytes.", RNS.LOG_ERROR) RNS.log(f"The encoded ID callsign for {self} exceeds the max length of {RNodeMultiInterface.CALLSIGN_MAX_LEN} bytes.", RNS.LOG_ERROR)
self.validcfg = False self.validcfg = False
else: else:
self.id_interval = None self.id_interval = None
self.id_callsign = None self.id_callsign = None
if (not self.validcfg): if (not self.validcfg):
raise ValueError("The configuration for "+str(self)+" contains errors, interface is offline") raise ValueError(f"The configuration for {self} contains errors, interface is offline")
def start(self): def start(self):
try: try:
@ -257,11 +257,11 @@ class RNodeMultiInterface(Interface):
if self.serial.is_open: if self.serial.is_open:
self.configure_device() self.configure_device()
else: else:
raise IOError("Could not open serial port") raise OSError("Could not open serial port")
except Exception as e: except Exception as e:
RNS.log("Could not open serial port for interface "+str(self), RNS.LOG_ERROR) RNS.log(f"Could not open serial port for interface {self}", RNS.LOG_ERROR)
RNS.log("The contained exception was: "+str(e), RNS.LOG_ERROR) RNS.log(f"The contained exception was: {e}", RNS.LOG_ERROR)
RNS.log("Reticulum will attempt to bring up this interface periodically", RNS.LOG_ERROR) RNS.log("Reticulum will attempt to bring up this interface periodically", RNS.LOG_ERROR)
if not self.detached and not self.reconnecting: if not self.detached and not self.reconnecting:
thread = threading.Thread(target=self.reconnect_port) thread = threading.Thread(target=self.reconnect_port)
@ -269,7 +269,7 @@ class RNodeMultiInterface(Interface):
thread.start() thread.start()
def open_port(self): def open_port(self):
RNS.log("Opening serial port "+self.port+"...") RNS.log(f"Opening serial port {self.port}...")
self.serial = self.pyserial.Serial( self.serial = self.pyserial.Serial(
port = self.port, port = self.port,
baudrate = self.speed, baudrate = self.speed,
@ -296,13 +296,13 @@ class RNodeMultiInterface(Interface):
sleep(0.2) sleep(0.2)
if not self.detected: if not self.detected:
RNS.log("Could not detect device for "+str(self), RNS.LOG_ERROR) RNS.log(f"Could not detect device for {self}", RNS.LOG_ERROR)
self.serial.close() self.serial.close()
else: else:
if self.platform == KISS.PLATFORM_ESP32 or self.platform == KISS.PLATFORM_NRF52: if self.platform == KISS.PLATFORM_ESP32 or self.platform == KISS.PLATFORM_NRF52:
self.display = True self.display = True
RNS.log("Serial port "+self.port+" is now open") RNS.log(f"Serial port {self.port} is now open")
RNS.log("Creating subinterfaces...", RNS.LOG_VERBOSE) RNS.log("Creating subinterfaces...", RNS.LOG_VERBOSE)
for subint in self.subint_config: for subint in self.subint_config:
subint_vport = int(subint[1]) subint_vport = int(subint[1])
@ -333,38 +333,38 @@ class RNodeMultiInterface(Interface):
interface.HW_MTU = self.HW_MTU interface.HW_MTU = self.HW_MTU
interface.detected = True interface.detected = True
RNS.Transport.interfaces.append(interface) RNS.Transport.interfaces.append(interface)
RNS.log("Spawned new RNode subinterface: "+str(interface), RNS.LOG_VERBOSE) RNS.log(f"Spawned new RNode subinterface: {interface}", RNS.LOG_VERBOSE)
self.clients += 1 self.clients += 1
else: else:
raise ValueError("Virtual port \""+subint[1]+"\" for subinterface "+subint[0]+" does not exist on "+self.name) raise ValueError(f"Virtual port \"{subint[1]}\" for subinterface {subint[0]} does not exist on {self.name}")
self.online = True self.online = True
def detect(self): def detect(self):
kiss_command = bytes([KISS.FEND, KISS.CMD_DETECT, KISS.DETECT_REQ, KISS.FEND, KISS.CMD_FW_VERSION, 0x00, KISS.FEND, KISS.CMD_PLATFORM, 0x00, KISS.FEND, KISS.CMD_MCU, 0x00, KISS.FEND, KISS.CMD_INTERFACES, 0x00, KISS.FEND]) kiss_command = bytes([KISS.FEND, KISS.CMD_DETECT, KISS.DETECT_REQ, KISS.FEND, KISS.CMD_FW_VERSION, 0x00, KISS.FEND, KISS.CMD_PLATFORM, 0x00, KISS.FEND, KISS.CMD_MCU, 0x00, KISS.FEND, KISS.CMD_INTERFACES, 0x00, KISS.FEND])
written = self.serial.write(kiss_command) written = self.serial.write(kiss_command)
if written != len(kiss_command): if written != len(kiss_command):
raise IOError("An IO error occurred while detecting hardware for "+str(self)) raise OSError(f"An IO error occurred while detecting hardware for {self}")
def leave(self): def leave(self):
kiss_command = bytes([KISS.FEND, KISS.CMD_LEAVE, 0xFF, KISS.FEND]) kiss_command = bytes([KISS.FEND, KISS.CMD_LEAVE, 0xFF, KISS.FEND])
written = self.serial.write(kiss_command) written = self.serial.write(kiss_command)
if written != len(kiss_command): if written != len(kiss_command):
raise IOError("An IO error occurred while sending host left command to device") raise OSError("An IO error occurred while sending host left command to device")
def enable_external_framebuffer(self): def enable_external_framebuffer(self):
if self.display != None: if self.display != None:
kiss_command = bytes([KISS.FEND, KISS.CMD_FB_EXT, 0x01, KISS.FEND]) kiss_command = bytes([KISS.FEND, KISS.CMD_FB_EXT, 0x01, KISS.FEND])
written = self.serial.write(kiss_command) written = self.serial.write(kiss_command)
if written != len(kiss_command): if written != len(kiss_command):
raise IOError("An IO error occurred while enabling external framebuffer on device") raise OSError("An IO error occurred while enabling external framebuffer on device")
def disable_external_framebuffer(self): def disable_external_framebuffer(self):
if self.display != None: if self.display != None:
kiss_command = bytes([KISS.FEND, KISS.CMD_FB_EXT, 0x00, KISS.FEND]) kiss_command = bytes([KISS.FEND, KISS.CMD_FB_EXT, 0x00, KISS.FEND])
written = self.serial.write(kiss_command) written = self.serial.write(kiss_command)
if written != len(kiss_command): if written != len(kiss_command):
raise IOError("An IO error occurred while disabling external framebuffer on device") raise OSError("An IO error occurred while disabling external framebuffer on device")
FB_PIXEL_WIDTH = 64 FB_PIXEL_WIDTH = 64
FB_BITS_PER_PIXEL = 1 FB_BITS_PER_PIXEL = 1
@ -388,13 +388,13 @@ class RNodeMultiInterface(Interface):
written = self.serial.write(kiss_command) written = self.serial.write(kiss_command)
if written != len(kiss_command): if written != len(kiss_command):
raise IOError("An IO error occurred while writing framebuffer data device") raise OSError("An IO error occurred while writing framebuffer data device")
def hard_reset(self): def hard_reset(self):
kiss_command = bytes([KISS.FEND, KISS.CMD_RESET, 0xf8, KISS.FEND]) kiss_command = bytes([KISS.FEND, KISS.CMD_RESET, 0xf8, KISS.FEND])
written = self.serial.write(kiss_command) written = self.serial.write(kiss_command)
if written != len(kiss_command): if written != len(kiss_command):
raise IOError("An IO error occurred while restarting device") raise OSError("An IO error occurred while restarting device")
sleep(2.25); sleep(2.25);
def setFrequency(self, frequency, interface): def setFrequency(self, frequency, interface):
@ -407,7 +407,7 @@ class RNodeMultiInterface(Interface):
kiss_command = bytes([KISS.FEND])+bytes([interface.sel_cmd])+bytes([KISS.FEND])+bytes([KISS.FEND])+bytes([KISS.CMD_FREQUENCY])+data+bytes([KISS.FEND]) kiss_command = bytes([KISS.FEND])+bytes([interface.sel_cmd])+bytes([KISS.FEND])+bytes([KISS.FEND])+bytes([KISS.CMD_FREQUENCY])+data+bytes([KISS.FEND])
written = self.serial.write(kiss_command) written = self.serial.write(kiss_command)
if written != len(kiss_command): if written != len(kiss_command):
raise IOError("An IO error occurred while configuring frequency for "+str(self)) raise OSError(f"An IO error occurred while configuring frequency for {self}")
self.selected_index = interface.index self.selected_index = interface.index
def setBandwidth(self, bandwidth, interface): def setBandwidth(self, bandwidth, interface):
@ -420,7 +420,7 @@ class RNodeMultiInterface(Interface):
kiss_command = bytes([KISS.FEND])+bytes([interface.sel_cmd])+bytes([KISS.FEND])+bytes([KISS.FEND])+bytes([KISS.CMD_BANDWIDTH])+data+bytes([KISS.FEND]) kiss_command = bytes([KISS.FEND])+bytes([interface.sel_cmd])+bytes([KISS.FEND])+bytes([KISS.FEND])+bytes([KISS.CMD_BANDWIDTH])+data+bytes([KISS.FEND])
written = self.serial.write(kiss_command) written = self.serial.write(kiss_command)
if written != len(kiss_command): if written != len(kiss_command):
raise IOError("An IO error occurred while configuring bandwidth for "+str(self)) raise OSError(f"An IO error occurred while configuring bandwidth for {self}")
self.selected_index = interface.index self.selected_index = interface.index
def setTXPower(self, txpower, interface): def setTXPower(self, txpower, interface):
@ -428,7 +428,7 @@ class RNodeMultiInterface(Interface):
kiss_command = bytes([KISS.FEND])+bytes([interface.sel_cmd])+bytes([KISS.FEND])+bytes([KISS.FEND])+bytes([KISS.CMD_TXPOWER])+txp+bytes([KISS.FEND]) kiss_command = bytes([KISS.FEND])+bytes([interface.sel_cmd])+bytes([KISS.FEND])+bytes([KISS.FEND])+bytes([KISS.CMD_TXPOWER])+txp+bytes([KISS.FEND])
written = self.serial.write(kiss_command) written = self.serial.write(kiss_command)
if written != len(kiss_command): if written != len(kiss_command):
raise IOError("An IO error occurred while configuring TX power for "+str(self)) raise OSError(f"An IO error occurred while configuring TX power for {self}")
self.selected_index = interface.index self.selected_index = interface.index
def setSpreadingFactor(self, sf, interface): def setSpreadingFactor(self, sf, interface):
@ -436,7 +436,7 @@ class RNodeMultiInterface(Interface):
kiss_command = bytes([KISS.FEND])+bytes([interface.sel_cmd])+bytes([KISS.FEND])+bytes([KISS.FEND])+bytes([KISS.CMD_SF])+sf+bytes([KISS.FEND]) kiss_command = bytes([KISS.FEND])+bytes([interface.sel_cmd])+bytes([KISS.FEND])+bytes([KISS.FEND])+bytes([KISS.CMD_SF])+sf+bytes([KISS.FEND])
written = self.serial.write(kiss_command) written = self.serial.write(kiss_command)
if written != len(kiss_command): if written != len(kiss_command):
raise IOError("An IO error occurred while configuring spreading factor for "+str(self)) raise OSError(f"An IO error occurred while configuring spreading factor for {self}")
self.selected_index = interface.index self.selected_index = interface.index
def setCodingRate(self, cr, interface): def setCodingRate(self, cr, interface):
@ -444,7 +444,7 @@ class RNodeMultiInterface(Interface):
kiss_command = bytes([KISS.FEND])+bytes([interface.sel_cmd])+bytes([KISS.FEND])+bytes([KISS.FEND])+bytes([KISS.CMD_CR])+cr+bytes([KISS.FEND]) kiss_command = bytes([KISS.FEND])+bytes([interface.sel_cmd])+bytes([KISS.FEND])+bytes([KISS.FEND])+bytes([KISS.CMD_CR])+cr+bytes([KISS.FEND])
written = self.serial.write(kiss_command) written = self.serial.write(kiss_command)
if written != len(kiss_command): if written != len(kiss_command):
raise IOError("An IO error occurred while configuring coding rate for "+str(self)) raise OSError(f"An IO error occurred while configuring coding rate for {self}")
self.selected_index = interface.index self.selected_index = interface.index
def setSTALock(self, st_alock, interface): def setSTALock(self, st_alock, interface):
@ -457,7 +457,7 @@ class RNodeMultiInterface(Interface):
kiss_command = bytes([KISS.FEND])+bytes([interface.sel_cmd])+bytes([KISS.FEND])+bytes([KISS.FEND])+bytes([KISS.CMD_ST_ALOCK])+data+bytes([KISS.FEND]) kiss_command = bytes([KISS.FEND])+bytes([interface.sel_cmd])+bytes([KISS.FEND])+bytes([KISS.FEND])+bytes([KISS.CMD_ST_ALOCK])+data+bytes([KISS.FEND])
written = self.serial.write(kiss_command) written = self.serial.write(kiss_command)
if written != len(kiss_command): if written != len(kiss_command):
raise IOError("An IO error occurred while configuring short-term airtime limit for "+str(self)) raise OSError(f"An IO error occurred while configuring short-term airtime limit for {self}")
self.selected_index = interface.index self.selected_index = interface.index
def setLTALock(self, lt_alock, interface): def setLTALock(self, lt_alock, interface):
@ -470,7 +470,7 @@ class RNodeMultiInterface(Interface):
kiss_command = bytes([KISS.FEND])+bytes([interface.sel_cmd])+bytes([KISS.FEND])+bytes([KISS.FEND])+bytes([KISS.CMD_LT_ALOCK])+data+bytes([KISS.FEND]) kiss_command = bytes([KISS.FEND])+bytes([interface.sel_cmd])+bytes([KISS.FEND])+bytes([KISS.FEND])+bytes([KISS.CMD_LT_ALOCK])+data+bytes([KISS.FEND])
written = self.serial.write(kiss_command) written = self.serial.write(kiss_command)
if written != len(kiss_command): if written != len(kiss_command):
raise IOError("An IO error occurred while configuring long-term airtime limit for "+str(self)) raise OSError(f"An IO error occurred while configuring long-term airtime limit for {self}")
self.selected_index = interface.index self.selected_index = interface.index
def setRadioState(self, state, interface): def setRadioState(self, state, interface):
@ -478,7 +478,7 @@ class RNodeMultiInterface(Interface):
kiss_command = bytes([KISS.FEND])+bytes([interface.sel_cmd])+bytes([KISS.FEND])+bytes([KISS.FEND])+bytes([KISS.CMD_RADIO_STATE])+bytes([state])+bytes([KISS.FEND]) kiss_command = bytes([KISS.FEND])+bytes([interface.sel_cmd])+bytes([KISS.FEND])+bytes([KISS.FEND])+bytes([KISS.CMD_RADIO_STATE])+bytes([state])+bytes([KISS.FEND])
written = self.serial.write(kiss_command) written = self.serial.write(kiss_command)
if written != len(kiss_command): if written != len(kiss_command):
raise IOError("An IO error occurred while configuring radio state for "+str(self)) raise OSError(f"An IO error occurred while configuring radio state for {self}")
self.selected_index = interface.index self.selected_index = interface.index
def validate_firmware(self): def validate_firmware(self):
@ -489,8 +489,8 @@ class RNodeMultiInterface(Interface):
if self.firmware_ok: if self.firmware_ok:
return return
RNS.log("The firmware version of the connected RNode is "+str(self.maj_version)+"."+str(self.min_version), RNS.LOG_ERROR) RNS.log(f"The firmware version of the connected RNode is {self.maj_version}.{self.min_version}", RNS.LOG_ERROR)
RNS.log("This version of Reticulum requires at least version "+str(RNodeMultiInterface.REQUIRED_FW_VER_MAJ)+"."+str(RNodeMultiInterface.REQUIRED_FW_VER_MIN), RNS.LOG_ERROR) RNS.log(f"This version of Reticulum requires at least version {RNodeMultiInterface.REQUIRED_FW_VER_MAJ}.{RNodeMultiInterface.REQUIRED_FW_VER_MIN}", RNS.LOG_ERROR)
RNS.log("Please update your RNode firmware with rnodeconf from https://github.com/markqvist/Reticulum/RNS/Utilities/rnodeconf.py") RNS.log("Please update your RNode firmware with rnodeconf from https://github.com/markqvist/Reticulum/RNS/Utilities/rnodeconf.py")
RNS.panic() RNS.panic()
@ -506,7 +506,7 @@ class RNodeMultiInterface(Interface):
self.txb += len(data) self.txb += len(data)
if written != len(frame): if written != len(frame):
raise IOError("Serial interface only wrote "+str(written)+" bytes of "+str(len(data))) raise OSError(f"Serial interface only wrote {written} bytes of {len(data)}")
def received_announce(self, from_spawned=False): def received_announce(self, from_spawned=False):
if from_spawned: self.ia_freq_deque.append(time.time()) if from_spawned: self.ia_freq_deque.append(time.time())
@ -589,7 +589,7 @@ class RNodeMultiInterface(Interface):
command_buffer = command_buffer+bytes([byte]) command_buffer = command_buffer+bytes([byte])
if (len(command_buffer) == 4): if (len(command_buffer) == 4):
self.subinterfaces[self.selected_index].r_frequency = command_buffer[0] << 24 | command_buffer[1] << 16 | command_buffer[2] << 8 | command_buffer[3] self.subinterfaces[self.selected_index].r_frequency = command_buffer[0] << 24 | command_buffer[1] << 16 | command_buffer[2] << 8 | command_buffer[3]
RNS.log(str(self.subinterfaces[self.selected_index])+" Radio reporting frequency is "+str(self.subinterfaces[self.selected_index].r_frequency/1000000.0)+" MHz", RNS.LOG_DEBUG) RNS.log(f"{self.subinterfaces[self.selected_index]} Radio reporting frequency is {self.subinterfaces[self.selected_index].r_frequency / 1000000.0} MHz", RNS.LOG_DEBUG)
self.subinterfaces[self.selected_index].updateBitrate() self.subinterfaces[self.selected_index].updateBitrate()
elif (command == KISS.CMD_BANDWIDTH): elif (command == KISS.CMD_BANDWIDTH):
@ -605,20 +605,20 @@ class RNodeMultiInterface(Interface):
command_buffer = command_buffer+bytes([byte]) command_buffer = command_buffer+bytes([byte])
if (len(command_buffer) == 4): if (len(command_buffer) == 4):
self.subinterfaces[self.selected_index].r_bandwidth = command_buffer[0] << 24 | command_buffer[1] << 16 | command_buffer[2] << 8 | command_buffer[3] self.subinterfaces[self.selected_index].r_bandwidth = command_buffer[0] << 24 | command_buffer[1] << 16 | command_buffer[2] << 8 | command_buffer[3]
RNS.log(str(self.subinterfaces[self.selected_index])+" Radio reporting bandwidth is "+str(self.subinterfaces[self.selected_index].r_bandwidth/1000.0)+" KHz", RNS.LOG_DEBUG) RNS.log(f"{self.subinterfaces[self.selected_index]} Radio reporting bandwidth is {self.subinterfaces[self.selected_index].r_bandwidth / 1000.0} KHz", RNS.LOG_DEBUG)
self.subinterfaces[self.selected_index].updateBitrate() self.subinterfaces[self.selected_index].updateBitrate()
elif (command == KISS.CMD_TXPOWER): elif (command == KISS.CMD_TXPOWER):
txp = byte - 256 if byte > 127 else byte txp = byte - 256 if byte > 127 else byte
self.subinterfaces[self.selected_index].r_txpower = txp self.subinterfaces[self.selected_index].r_txpower = txp
RNS.log(str(self.subinterfaces[self.selected_index])+" Radio reporting TX power is "+str(self.subinterfaces[self.selected_index].r_txpower)+" dBm", RNS.LOG_DEBUG) RNS.log(f"{self.subinterfaces[self.selected_index]} Radio reporting TX power is {self.subinterfaces[self.selected_index].r_txpower} dBm", RNS.LOG_DEBUG)
elif (command == KISS.CMD_SF): elif (command == KISS.CMD_SF):
self.subinterfaces[self.selected_index].r_sf = byte self.subinterfaces[self.selected_index].r_sf = byte
RNS.log(str(self.subinterfaces[self.selected_index])+" Radio reporting spreading factor is "+str(self.subinterfaces[self.selected_index].r_sf), RNS.LOG_DEBUG) RNS.log(f"{self.subinterfaces[self.selected_index]} Radio reporting spreading factor is {self.subinterfaces[self.selected_index].r_sf}", RNS.LOG_DEBUG)
self.subinterfaces[self.selected_index].updateBitrate() self.subinterfaces[self.selected_index].updateBitrate()
elif (command == KISS.CMD_CR): elif (command == KISS.CMD_CR):
self.subinterfaces[self.selected_index].r_cr = byte self.subinterfaces[self.selected_index].r_cr = byte
RNS.log(str(self.subinterfaces[self.selected_index])+" Radio reporting coding rate is "+str(self.subinterfaces[self.selected_index].r_cr), RNS.LOG_DEBUG) RNS.log(f"{self.subinterfaces[self.selected_index]} Radio reporting coding rate is {self.subinterfaces[self.selected_index].r_cr}", RNS.LOG_DEBUG)
self.subinterfaces[self.selected_index].updateBitrate() self.subinterfaces[self.selected_index].updateBitrate()
elif (command == KISS.CMD_RADIO_STATE): elif (command == KISS.CMD_RADIO_STATE):
self.subinterfaces[self.selected_index].r_state = byte self.subinterfaces[self.selected_index].r_state = byte
@ -626,7 +626,7 @@ class RNodeMultiInterface(Interface):
pass pass
#RNS.log(str(self)+" Radio reporting state is online", RNS.LOG_DEBUG) #RNS.log(str(self)+" Radio reporting state is online", RNS.LOG_DEBUG)
else: else:
RNS.log(str(self.subinterfaces[self.selected_index])+" Radio reporting state is offline", RNS.LOG_DEBUG) RNS.log(f"{self.subinterfaces[self.selected_index]} Radio reporting state is offline", RNS.LOG_DEBUG)
elif (command == KISS.CMD_RADIO_LOCK): elif (command == KISS.CMD_RADIO_LOCK):
self.subinterfaces[self.selected_index].r_lock = byte self.subinterfaces[self.selected_index].r_lock = byte
@ -705,7 +705,7 @@ class RNodeMultiInterface(Interface):
if (len(command_buffer) == 2): if (len(command_buffer) == 2):
at = command_buffer[0] << 8 | command_buffer[1] at = command_buffer[0] << 8 | command_buffer[1]
self.subinterfaces[self.selected_index].r_st_alock = at/100.0 self.subinterfaces[self.selected_index].r_st_alock = at/100.0
RNS.log(str(self.subinterfaces[self.selected_index])+" Radio reporting short-term airtime limit is "+str(self.subinterfaces[self.selected_index].r_st_alock)+"%", RNS.LOG_DEBUG) RNS.log(f"{self.subinterfaces[self.selected_index]} Radio reporting short-term airtime limit is {self.subinterfaces[self.selected_index].r_st_alock}%", RNS.LOG_DEBUG)
elif (command == KISS.CMD_LT_ALOCK): elif (command == KISS.CMD_LT_ALOCK):
if (byte == KISS.FESC): if (byte == KISS.FESC):
escape = True escape = True
@ -720,7 +720,7 @@ class RNodeMultiInterface(Interface):
if (len(command_buffer) == 2): if (len(command_buffer) == 2):
at = command_buffer[0] << 8 | command_buffer[1] at = command_buffer[0] << 8 | command_buffer[1]
self.subinterfaces[self.selected_index].r_lt_alock = at/100.0 self.subinterfaces[self.selected_index].r_lt_alock = at/100.0
RNS.log(str(self.subinterfaces[self.selected_index])+" Radio reporting long-term airtime limit is "+str(self.subinterfaces[self.selected_index].r_lt_alock)+"%", RNS.LOG_DEBUG) RNS.log(f"{self.subinterfaces[self.selected_index]} Radio reporting long-term airtime limit is {self.subinterfaces[self.selected_index].r_lt_alock}%", RNS.LOG_DEBUG)
elif (command == KISS.CMD_STAT_CHTM): elif (command == KISS.CMD_STAT_CHTM):
if (byte == KISS.FESC): if (byte == KISS.FESC):
escape = True escape = True
@ -766,9 +766,9 @@ class RNodeMultiInterface(Interface):
self.subinterfaces[self.selected_index].r_preamble_symbols = prs self.subinterfaces[self.selected_index].r_preamble_symbols = prs
self.subinterfaces[self.selected_index].r_premable_time_ms = prt self.subinterfaces[self.selected_index].r_premable_time_ms = prt
self.subinterfaces[self.selected_index].r_csma_slot_time_ms = cst self.subinterfaces[self.selected_index].r_csma_slot_time_ms = cst
RNS.log(str(self.subinterfaces[self.selected_index])+" Radio reporting symbol time is "+str(round(self.subinterfaces[self.selected_index].r_symbol_time_ms,2))+"ms (at "+str(self.subinterfaces[self.selected_index].r_symbol_rate)+" baud)", RNS.LOG_DEBUG) RNS.log(f"{self.subinterfaces[self.selected_index]} Radio reporting symbol time is {round(self.subinterfaces[self.selected_index].r_symbol_time_ms, 2)}ms (at {self.subinterfaces[self.selected_index].r_symbol_rate} baud)", RNS.LOG_DEBUG)
RNS.log(str(self.subinterfaces[self.selected_index])+" Radio reporting preamble is "+str(self.subinterfaces[self.selected_index].r_preamble_symbols)+" symbols ("+str(self.subinterfaces[self.selected_index].r_premable_time_ms)+"ms)", RNS.LOG_DEBUG) RNS.log(f"{self.subinterfaces[self.selected_index]} Radio reporting preamble is {self.subinterfaces[self.selected_index].r_preamble_symbols} symbols ({self.subinterfaces[self.selected_index].r_premable_time_ms}ms)", RNS.LOG_DEBUG)
RNS.log(str(self.subinterfaces[self.selected_index])+" Radio reporting CSMA slot time is "+str(self.subinterfaces[self.selected_index].r_csma_slot_time_ms)+"ms", RNS.LOG_DEBUG) RNS.log(f"{self.subinterfaces[self.selected_index]} Radio reporting CSMA slot time is {self.subinterfaces[self.selected_index].r_csma_slot_time_ms}ms", RNS.LOG_DEBUG)
elif (command == KISS.CMD_RANDOM): elif (command == KISS.CMD_RANDOM):
self.r_random = byte self.r_random = byte
elif (command == KISS.CMD_PLATFORM): elif (command == KISS.CMD_PLATFORM):
@ -777,20 +777,20 @@ class RNodeMultiInterface(Interface):
self.mcu = byte self.mcu = byte
elif (command == KISS.CMD_ERROR): elif (command == KISS.CMD_ERROR):
if (byte == KISS.ERROR_INITRADIO): if (byte == KISS.ERROR_INITRADIO):
RNS.log(str(self)+" hardware initialisation error (code "+RNS.hexrep(byte)+")", RNS.LOG_ERROR) RNS.log(f"{self} hardware initialisation error (code {RNS.hexrep(byte)})", RNS.LOG_ERROR)
raise IOError("Radio initialisation failure") raise OSError("Radio initialisation failure")
elif (byte == KISS.ERROR_TXFAILED): elif (byte == KISS.ERROR_TXFAILED):
RNS.log(str(self)+" hardware TX error (code "+RNS.hexrep(byte)+")", RNS.LOG_ERROR) RNS.log(f"{self} hardware TX error (code {RNS.hexrep(byte)})", RNS.LOG_ERROR)
raise IOError("Hardware transmit failure") raise OSError("Hardware transmit failure")
else: else:
RNS.log(str(self)+" hardware error (code "+RNS.hexrep(byte)+")", RNS.LOG_ERROR) RNS.log(f"{self} hardware error (code {RNS.hexrep(byte)})", RNS.LOG_ERROR)
raise IOError("Unknown hardware failure") raise OSError("Unknown hardware failure")
elif (command == KISS.CMD_RESET): elif (command == KISS.CMD_RESET):
if (byte == 0xF8): if (byte == 0xF8):
if self.platform == KISS.PLATFORM_ESP32: if self.platform == KISS.PLATFORM_ESP32:
if self.online: if self.online:
RNS.log("Detected reset while device was online, reinitialising device...", RNS.LOG_ERROR) RNS.log("Detected reset while device was online, reinitialising device...", RNS.LOG_ERROR)
raise IOError("ESP32 reset") raise OSError("ESP32 reset")
elif (command == KISS.CMD_READY): elif (command == KISS.CMD_READY):
self.process_queue() self.process_queue()
elif (command == KISS.CMD_DETECT): elif (command == KISS.CMD_DETECT):
@ -808,7 +808,7 @@ class RNodeMultiInterface(Interface):
else: else:
time_since_last = int(time.time()*1000) - last_read_ms time_since_last = int(time.time()*1000) - last_read_ms
if len(data_buffer) > 0 and time_since_last > self.timeout: if len(data_buffer) > 0 and time_since_last > self.timeout:
RNS.log(str(self)+" serial read timeout in command "+str(command), RNS.LOG_WARNING) RNS.log(f"{self} serial read timeout in command {command}", RNS.LOG_WARNING)
data_buffer = b"" data_buffer = b""
in_frame = False in_frame = False
command = KISS.CMD_UNKNOWN command = KISS.CMD_UNKNOWN
@ -824,14 +824,14 @@ class RNodeMultiInterface(Interface):
self.subinterfaces[interface.index].processOutgoing(self.id_callsign) self.subinterfaces[interface.index].processOutgoing(self.id_callsign)
if interface_available: if interface_available:
RNS.log("Interface "+str(self)+" is transmitting beacon data on all subinterfaces: "+str(self.id_callsign.decode("utf-8")), RNS.LOG_DEBUG) RNS.log(f"Interface {self} is transmitting beacon data on all subinterfaces: {self.id_callsign.decode('utf-8')}", RNS.LOG_DEBUG)
sleep(0.08) sleep(0.08)
except Exception as e: except Exception as e:
self.online = False self.online = False
RNS.log("A serial port error occurred, the contained exception was: "+str(e), RNS.LOG_ERROR) RNS.log(f"A serial port error occurred, the contained exception was: {e}", RNS.LOG_ERROR)
RNS.log("The interface "+str(self)+" experienced an unrecoverable error and is now offline.", RNS.LOG_ERROR) RNS.log(f"The interface {self} experienced an unrecoverable error and is now offline.", RNS.LOG_ERROR)
if RNS.Reticulum.panic_on_interface_error: if RNS.Reticulum.panic_on_interface_error:
RNS.panic() RNS.panic()
@ -854,16 +854,16 @@ class RNodeMultiInterface(Interface):
while not self.online and not self.detached: while not self.online and not self.detached:
try: try:
time.sleep(5) time.sleep(5)
RNS.log("Attempting to reconnect serial port "+str(self.port)+" for "+str(self)+"...", RNS.LOG_VERBOSE) RNS.log(f"Attempting to reconnect serial port {self.port} for {self}...", RNS.LOG_VERBOSE)
self.open_port() self.open_port()
if self.serial.is_open: if self.serial.is_open:
self.configure_device() self.configure_device()
except Exception as e: except Exception as e:
RNS.log("Error while reconnecting port, the contained exception was: "+str(e), RNS.LOG_ERROR) RNS.log(f"Error while reconnecting port, the contained exception was: {e}", RNS.LOG_ERROR)
self.reconnecting = False self.reconnecting = False
if self.online: if self.online:
RNS.log("Reconnected serial port for "+str(self)) RNS.log(f"Reconnected serial port for {self}")
def detach(self): def detach(self):
self.detached = True self.detached = True
@ -890,7 +890,7 @@ class RNodeMultiInterface(Interface):
interface.process_queue() interface.process_queue()
def __str__(self): def __str__(self):
return "RNodeMultiInterface["+str(self.name)+"]" return f"RNodeMultiInterface[{self.name}]"
class RNodeSubInterface(Interface): class RNodeSubInterface(Interface):
LOW_FREQ_MIN = 137000000 LOW_FREQ_MIN = 137000000
@ -1019,42 +1019,42 @@ class RNodeSubInterface(Interface):
self.validcfg = True self.validcfg = True
if (self.interface_type == "SX126X" or self.interface_type == "SX127X"): if (self.interface_type == "SX126X" or self.interface_type == "SX127X"):
if (self.frequency < RNodeSubInterface.LOW_FREQ_MIN or self.frequency > RNodeSubInterface.LOW_FREQ_MAX): if (self.frequency < RNodeSubInterface.LOW_FREQ_MIN or self.frequency > RNodeSubInterface.LOW_FREQ_MAX):
RNS.log("Invalid frequency configured for "+str(self), RNS.LOG_ERROR) RNS.log(f"Invalid frequency configured for {self}", RNS.LOG_ERROR)
self.validcfg = False self.validcfg = False
elif (self.interface_type == "SX128X"): elif (self.interface_type == "SX128X"):
if (self.frequency < RNodeSubInterface.HIGH_FREQ_MIN or self.frequency > RNodeSubInterface.HIGH_FREQ_MAX): if (self.frequency < RNodeSubInterface.HIGH_FREQ_MIN or self.frequency > RNodeSubInterface.HIGH_FREQ_MAX):
RNS.log("Invalid frequency configured for "+str(self), RNS.LOG_ERROR) RNS.log(f"Invalid frequency configured for {self}", RNS.LOG_ERROR)
self.validcfg = False self.validcfg = False
else: else:
RNS.log("Invalid interface type configured for "+str(self), RNS.LOG_ERROR) RNS.log(f"Invalid interface type configured for {self}", RNS.LOG_ERROR)
self.validcfg = False self.validcfg = False
if (self.txpower < -9 or self.txpower > 27): if (self.txpower < -9 or self.txpower > 27):
RNS.log("Invalid TX power configured for "+str(self), RNS.LOG_ERROR) RNS.log(f"Invalid TX power configured for {self}", RNS.LOG_ERROR)
self.validcfg = False self.validcfg = False
if (self.bandwidth < 7800 or self.bandwidth > 1625000): if (self.bandwidth < 7800 or self.bandwidth > 1625000):
RNS.log("Invalid bandwidth configured for "+str(self), RNS.LOG_ERROR) RNS.log(f"Invalid bandwidth configured for {self}", RNS.LOG_ERROR)
self.validcfg = False self.validcfg = False
if (self.sf < 5 or self.sf > 12): if (self.sf < 5 or self.sf > 12):
RNS.log("Invalid spreading factor configured for "+str(self), RNS.LOG_ERROR) RNS.log(f"Invalid spreading factor configured for {self}", RNS.LOG_ERROR)
self.validcfg = False self.validcfg = False
if (self.cr < 5 or self.cr > 8): if (self.cr < 5 or self.cr > 8):
RNS.log("Invalid coding rate configured for "+str(self), RNS.LOG_ERROR) RNS.log(f"Invalid coding rate configured for {self}", RNS.LOG_ERROR)
self.validcfg = False self.validcfg = False
if (self.st_alock and (self.st_alock < 0.0 or self.st_alock > 100.0)): if (self.st_alock and (self.st_alock < 0.0 or self.st_alock > 100.0)):
RNS.log("Invalid short-term airtime limit configured for "+str(self), RNS.LOG_ERROR) RNS.log(f"Invalid short-term airtime limit configured for {self}", RNS.LOG_ERROR)
self.validcfg = False self.validcfg = False
if (self.lt_alock and (self.lt_alock < 0.0 or self.lt_alock > 100.0)): if (self.lt_alock and (self.lt_alock < 0.0 or self.lt_alock > 100.0)):
RNS.log("Invalid long-term airtime limit configured for "+str(self), RNS.LOG_ERROR) RNS.log(f"Invalid long-term airtime limit configured for {self}", RNS.LOG_ERROR)
self.validcfg = False self.validcfg = False
if (not self.validcfg): if (not self.validcfg):
raise ValueError("The configuration for "+str(self)+" contains errors, interface is offline") raise ValueError(f"The configuration for {self} contains errors, interface is offline")
self.configure_device() self.configure_device()
@ -1068,15 +1068,15 @@ class RNodeSubInterface(Interface):
self.r_lock = None self.r_lock = None
sleep(2.0) sleep(2.0)
RNS.log("Configuring RNode subinterface "+str(self)+"...", RNS.LOG_VERBOSE) RNS.log(f"Configuring RNode subinterface {self}...", RNS.LOG_VERBOSE)
self.initRadio() self.initRadio()
if (self.validateRadioState()): if (self.validateRadioState()):
self.interface_ready = True self.interface_ready = True
RNS.log(str(self)+" is configured and powered up") RNS.log(f"{self} is configured and powered up")
sleep(0.3) sleep(0.3)
self.online = True self.online = True
else: else:
RNS.log("After configuring "+str(self)+", the reported radio parameters did not match your configuration.", RNS.LOG_ERROR) RNS.log(f"After configuring {self}, the reported radio parameters did not match your configuration.", RNS.LOG_ERROR)
RNS.log("Make sure that your hardware actually supports the parameters specified in the configuration", RNS.LOG_ERROR) RNS.log("Make sure that your hardware actually supports the parameters specified in the configuration", RNS.LOG_ERROR)
RNS.log("Aborting RNode startup", RNS.LOG_ERROR) RNS.log("Aborting RNode startup", RNS.LOG_ERROR)
@ -1093,7 +1093,7 @@ class RNodeSubInterface(Interface):
self.state = KISS.RADIO_STATE_ON self.state = KISS.RADIO_STATE_ON
def validateRadioState(self): def validateRadioState(self):
RNS.log("Waiting for radio configuration validation for "+str(self)+"...", RNS.LOG_VERBOSE) RNS.log(f"Waiting for radio configuration validation for {self}...", RNS.LOG_VERBOSE)
sleep(0.25); sleep(0.25);
self.validcfg = True self.validcfg = True
@ -1123,7 +1123,7 @@ class RNodeSubInterface(Interface):
try: try:
self.bitrate = self.r_sf * ( (4.0/self.r_cr) / (math.pow(2,self.r_sf)/(self.r_bandwidth/1000)) ) * 1000 self.bitrate = self.r_sf * ( (4.0/self.r_cr) / (math.pow(2,self.r_sf)/(self.r_bandwidth/1000)) ) * 1000
self.bitrate_kbps = round(self.bitrate/1000.0, 2) self.bitrate_kbps = round(self.bitrate/1000.0, 2)
RNS.log(str(self)+" On-air bitrate is now "+str(self.bitrate_kbps)+ " kbps", RNS.LOG_VERBOSE) RNS.log(f"{self} On-air bitrate is now {self.bitrate_kbps} kbps", RNS.LOG_VERBOSE)
except: except:
self.bitrate = 0 self.bitrate = 0
@ -1162,4 +1162,4 @@ class RNodeSubInterface(Interface):
self.interface_ready = True self.interface_ready = True
def __str__(self): def __str__(self):
return self.parent_interface.name+"["+self.name+"]" return f"{self.parent_interface.name}[{self.name}]"

View File

@ -86,17 +86,17 @@ class SerialInterface(Interface):
try: try:
self.open_port() self.open_port()
except Exception as e: except Exception as e:
RNS.log("Could not open serial port for interface "+str(self), RNS.LOG_ERROR) RNS.log(f"Could not open serial port for interface {self}", RNS.LOG_ERROR)
raise e raise e
if self.serial.is_open: if self.serial.is_open:
self.configure_device() self.configure_device()
else: else:
raise IOError("Could not open serial port") raise OSError("Could not open serial port")
def open_port(self): def open_port(self):
RNS.log("Opening serial port "+self.port+"...", RNS.LOG_VERBOSE) RNS.log(f"Opening serial port {self.port}...", RNS.LOG_VERBOSE)
self.serial = self.pyserial.Serial( self.serial = self.pyserial.Serial(
port = self.port, port = self.port,
baudrate = self.speed, baudrate = self.speed,
@ -118,7 +118,7 @@ class SerialInterface(Interface):
thread.daemon = True thread.daemon = True
thread.start() thread.start()
self.online = True self.online = True
RNS.log("Serial port "+self.port+" is now open", RNS.LOG_VERBOSE) RNS.log(f"Serial port {self.port} is now open", RNS.LOG_VERBOSE)
def processIncoming(self, data): def processIncoming(self, data):
@ -132,7 +132,7 @@ class SerialInterface(Interface):
written = self.serial.write(data) written = self.serial.write(data)
self.txb += len(data) self.txb += len(data)
if written != len(data): if written != len(data):
raise IOError("Serial interface only wrote "+str(written)+" bytes of "+str(len(data))) raise OSError(f"Serial interface only wrote {written} bytes of {len(data)}")
def readLoop(self): def readLoop(self):
@ -175,8 +175,8 @@ class SerialInterface(Interface):
except Exception as e: except Exception as e:
self.online = False self.online = False
RNS.log("A serial port error occurred, the contained exception was: "+str(e), RNS.LOG_ERROR) RNS.log(f"A serial port error occurred, the contained exception was: {e}", RNS.LOG_ERROR)
RNS.log("The interface "+str(self)+" experienced an unrecoverable error and is now offline.", RNS.LOG_ERROR) RNS.log(f"The interface {self} experienced an unrecoverable error and is now offline.", RNS.LOG_ERROR)
if RNS.Reticulum.panic_on_interface_error: if RNS.Reticulum.panic_on_interface_error:
RNS.panic() RNS.panic()
@ -191,17 +191,17 @@ class SerialInterface(Interface):
while not self.online: while not self.online:
try: try:
time.sleep(5) time.sleep(5)
RNS.log("Attempting to reconnect serial port "+str(self.port)+" for "+str(self)+"...", RNS.LOG_VERBOSE) RNS.log(f"Attempting to reconnect serial port {self.port} for {self}...", RNS.LOG_VERBOSE)
self.open_port() self.open_port()
if self.serial.is_open: if self.serial.is_open:
self.configure_device() self.configure_device()
except Exception as e: except Exception as e:
RNS.log("Error while reconnecting port, the contained exception was: "+str(e), RNS.LOG_ERROR) RNS.log(f"Error while reconnecting port, the contained exception was: {e}", RNS.LOG_ERROR)
RNS.log("Reconnected serial port for "+str(self)) RNS.log(f"Reconnected serial port for {self}")
def should_ingress_limit(self): def should_ingress_limit(self):
return False return False
def __str__(self): def __str__(self):
return "SerialInterface["+self.name+"]" return f"SerialInterface[{self.name}]"

View File

@ -180,25 +180,25 @@ class TCPClientInterface(Interface):
if self.socket != None: if self.socket != None:
if hasattr(self.socket, "close"): if hasattr(self.socket, "close"):
if callable(self.socket.close): if callable(self.socket.close):
RNS.log("Detaching "+str(self), RNS.LOG_DEBUG) RNS.log(f"Detaching {self}", RNS.LOG_DEBUG)
self.detached = True self.detached = True
try: try:
self.socket.shutdown(socket.SHUT_RDWR) self.socket.shutdown(socket.SHUT_RDWR)
except Exception as e: except Exception as e:
RNS.log("Error while shutting down socket for "+str(self)+": "+str(e)) RNS.log(f"Error while shutting down socket for {self}: {e}")
try: try:
self.socket.close() self.socket.close()
except Exception as e: except Exception as e:
RNS.log("Error while closing socket for "+str(self)+": "+str(e)) RNS.log(f"Error while closing socket for {self}: {e}")
self.socket = None self.socket = None
def connect(self, initial=False): def connect(self, initial=False):
try: try:
if initial: if initial:
RNS.log("Establishing TCP connection for "+str(self)+"...", RNS.LOG_DEBUG) RNS.log(f"Establishing TCP connection for {self}...", RNS.LOG_DEBUG)
self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.socket.settimeout(TCPClientInterface.INITIAL_CONNECT_TIMEOUT) self.socket.settimeout(TCPClientInterface.INITIAL_CONNECT_TIMEOUT)
@ -208,12 +208,12 @@ class TCPClientInterface(Interface):
self.online = True self.online = True
if initial: if initial:
RNS.log("TCP connection for "+str(self)+" established", RNS.LOG_DEBUG) RNS.log(f"TCP connection for {self} established", RNS.LOG_DEBUG)
except Exception as e: except Exception as e:
if initial: if initial:
RNS.log("Initial connection for "+str(self)+" could not be established: "+str(e), RNS.LOG_ERROR) RNS.log(f"Initial connection for {self} could not be established: {e}", RNS.LOG_ERROR)
RNS.log("Leaving unconnected and retrying connection in "+str(TCPClientInterface.RECONNECT_WAIT)+" seconds.", RNS.LOG_ERROR) RNS.log(f"Leaving unconnected and retrying connection in {TCPClientInterface.RECONNECT_WAIT} seconds.", RNS.LOG_ERROR)
return False return False
else: else:
@ -241,7 +241,7 @@ class TCPClientInterface(Interface):
attempts += 1 attempts += 1
if self.max_reconnect_tries != None and attempts > self.max_reconnect_tries: if self.max_reconnect_tries != None and attempts > self.max_reconnect_tries:
RNS.log("Max reconnection attempts reached for "+str(self), RNS.LOG_ERROR) RNS.log(f"Max reconnection attempts reached for {self}", RNS.LOG_ERROR)
self.teardown() self.teardown()
break break
@ -249,10 +249,10 @@ class TCPClientInterface(Interface):
self.connect() self.connect()
except Exception as e: except Exception as e:
RNS.log("Connection attempt for "+str(self)+" failed: "+str(e), RNS.LOG_DEBUG) RNS.log(f"Connection attempt for {self} failed: {e}", RNS.LOG_DEBUG)
if not self.never_connected: if not self.never_connected:
RNS.log("Reconnected socket for "+str(self)+".", RNS.LOG_INFO) RNS.log(f"Reconnected socket for {self}.", RNS.LOG_INFO)
self.reconnecting = False self.reconnecting = False
thread = threading.Thread(target=self.read_loop) thread = threading.Thread(target=self.read_loop)
@ -263,7 +263,7 @@ class TCPClientInterface(Interface):
else: else:
RNS.log("Attempt to reconnect on a non-initiator TCP interface. This should not happen.", RNS.LOG_ERROR) RNS.log("Attempt to reconnect on a non-initiator TCP interface. This should not happen.", RNS.LOG_ERROR)
raise IOError("Attempt to reconnect on a non-initiator TCP interface") raise OSError("Attempt to reconnect on a non-initiator TCP interface")
def processIncoming(self, data): def processIncoming(self, data):
self.rxb += len(data) self.rxb += len(data)
@ -292,8 +292,8 @@ class TCPClientInterface(Interface):
self.parent_interface.txb += len(data) self.parent_interface.txb += len(data)
except Exception as e: except Exception as e:
RNS.log("Exception occurred while transmitting via "+str(self)+", tearing down interface", RNS.LOG_ERROR) RNS.log(f"Exception occurred while transmitting via {self}, tearing down interface", RNS.LOG_ERROR)
RNS.log("The contained exception was: "+str(e), RNS.LOG_ERROR) RNS.log(f"The contained exception was: {e}", RNS.LOG_ERROR)
self.teardown() self.teardown()
@ -361,10 +361,10 @@ class TCPClientInterface(Interface):
else: else:
self.online = False self.online = False
if self.initiator and not self.detached: if self.initiator and not self.detached:
RNS.log("The socket for "+str(self)+" was closed, attempting to reconnect...", RNS.LOG_WARNING) RNS.log(f"The socket for {self} was closed, attempting to reconnect...", RNS.LOG_WARNING)
self.reconnect() self.reconnect()
else: else:
RNS.log("The socket for remote client "+str(self)+" was closed.", RNS.LOG_VERBOSE) RNS.log(f"The socket for remote client {self} was closed.", RNS.LOG_VERBOSE)
self.teardown() self.teardown()
break break
@ -372,7 +372,7 @@ class TCPClientInterface(Interface):
except Exception as e: except Exception as e:
self.online = False self.online = False
RNS.log("An interface error occurred for "+str(self)+", the contained exception was: "+str(e), RNS.LOG_WARNING) RNS.log(f"An interface error occurred for {self}, the contained exception was: {e}", RNS.LOG_WARNING)
if self.initiator: if self.initiator:
RNS.log("Attempting to reconnect...", RNS.LOG_WARNING) RNS.log("Attempting to reconnect...", RNS.LOG_WARNING)
@ -382,12 +382,12 @@ class TCPClientInterface(Interface):
def teardown(self): def teardown(self):
if self.initiator and not self.detached: if self.initiator and not self.detached:
RNS.log("The interface "+str(self)+" experienced an unrecoverable error and is being torn down. Restart Reticulum to attempt to open this interface again.", RNS.LOG_ERROR) RNS.log(f"The interface {self} experienced an unrecoverable error and is being torn down. Restart Reticulum to attempt to open this interface again.", RNS.LOG_ERROR)
if RNS.Reticulum.panic_on_interface_error: if RNS.Reticulum.panic_on_interface_error:
RNS.panic() RNS.panic()
else: else:
RNS.log("The interface "+str(self)+" is being torn down.", RNS.LOG_VERBOSE) RNS.log(f"The interface {self} is being torn down.", RNS.LOG_VERBOSE)
self.online = False self.online = False
self.OUT = False self.OUT = False
@ -402,7 +402,7 @@ class TCPClientInterface(Interface):
def __str__(self): def __str__(self):
return "TCPInterface["+str(self.name)+"/"+str(self.target_ip)+":"+str(self.target_port)+"]" return f"TCPInterface[{self.name}/{self.target_ip}:{self.target_port}]"
class TCPServerInterface(Interface): class TCPServerInterface(Interface):
@ -466,7 +466,7 @@ class TCPServerInterface(Interface):
def incoming_connection(self, handler): def incoming_connection(self, handler):
RNS.log("Accepting incoming TCP connection", RNS.LOG_VERBOSE) RNS.log("Accepting incoming TCP connection", RNS.LOG_VERBOSE)
interface_name = "Client on "+self.name interface_name = f"Client on {self.name}"
spawned_interface = TCPClientInterface(self.owner, interface_name, target_ip=None, target_port=None, connected_socket=handler.request, i2p_tunneled=self.i2p_tunneled) spawned_interface = TCPClientInterface(self.owner, interface_name, target_ip=None, target_port=None, connected_socket=handler.request, i2p_tunneled=self.i2p_tunneled)
spawned_interface.OUT = self.OUT spawned_interface.OUT = self.OUT
spawned_interface.IN = self.IN spawned_interface.IN = self.IN
@ -501,7 +501,7 @@ class TCPServerInterface(Interface):
spawned_interface.mode = self.mode spawned_interface.mode = self.mode
spawned_interface.HW_MTU = self.HW_MTU spawned_interface.HW_MTU = self.HW_MTU
spawned_interface.online = True spawned_interface.online = True
RNS.log("Spawned new TCPClient Interface: "+str(spawned_interface), RNS.LOG_VERBOSE) RNS.log(f"Spawned new TCPClient Interface: {spawned_interface}", RNS.LOG_VERBOSE)
RNS.Transport.interfaces.append(spawned_interface) RNS.Transport.interfaces.append(spawned_interface)
self.clients += 1 self.clients += 1
spawned_interface.read_loop() spawned_interface.read_loop()
@ -521,17 +521,17 @@ class TCPServerInterface(Interface):
if hasattr(self.server, "shutdown"): if hasattr(self.server, "shutdown"):
if callable(self.server.shutdown): if callable(self.server.shutdown):
try: try:
RNS.log("Detaching "+str(self), RNS.LOG_DEBUG) RNS.log(f"Detaching {self}", RNS.LOG_DEBUG)
self.server.shutdown() self.server.shutdown()
self.detached = True self.detached = True
self.server = None self.server = None
except Exception as e: except Exception as e:
RNS.log("Error while shutting down server for "+str(self)+": "+str(e)) RNS.log(f"Error while shutting down server for {self}: {e}")
def __str__(self): def __str__(self):
return "TCPServerInterface["+self.name+"/"+self.bind_ip+":"+str(self.bind_port)+"]" return f"TCPServerInterface[{self.name}/{self.bind_ip}:{self.bind_port}]"
class TCPInterfaceHandler(socketserver.BaseRequestHandler): class TCPInterfaceHandler(socketserver.BaseRequestHandler):

View File

@ -101,11 +101,11 @@ class UDPInterface(Interface):
self.txb += len(data) self.txb += len(data)
except Exception as e: except Exception as e:
RNS.log("Could not transmit on "+str(self)+". The contained exception was: "+str(e), RNS.LOG_ERROR) RNS.log(f"Could not transmit on {self}. The contained exception was: {e}", RNS.LOG_ERROR)
def __str__(self): def __str__(self):
return "UDPInterface["+self.name+"/"+self.bind_ip+":"+str(self.bind_port)+"]" return f"UDPInterface[{self.name}/{self.bind_ip}:{self.bind_port}]"
class UDPInterfaceHandler(socketserver.BaseRequestHandler): class UDPInterfaceHandler(socketserver.BaseRequestHandler):
def __init__(self, callback, *args, **keys): def __init__(self, callback, *args, **keys):

View File

@ -24,5 +24,5 @@ import os
import glob import glob
import RNS.Interfaces.Android import RNS.Interfaces.Android
modules = glob.glob(os.path.dirname(__file__)+"/*.py") modules = glob.glob(f"{os.path.dirname(__file__)}/*.py")
__all__ = [ os.path.basename(f)[:-3] for f in modules if not f.endswith('__init__.py')] __all__ = [ os.path.basename(f)[:-3] for f in modules if not f.endswith('__init__.py')]

View File

@ -115,8 +115,8 @@ class Link:
link.destination = packet.destination link.destination = packet.destination
link.establishment_timeout = Link.ESTABLISHMENT_TIMEOUT_PER_HOP * max(1, packet.hops) + Link.KEEPALIVE link.establishment_timeout = Link.ESTABLISHMENT_TIMEOUT_PER_HOP * max(1, packet.hops) + Link.KEEPALIVE
link.establishment_cost += len(packet.raw) link.establishment_cost += len(packet.raw)
RNS.log("Validating link request "+RNS.prettyhexrep(link.link_id), RNS.LOG_VERBOSE) RNS.log(f"Validating link request {RNS.prettyhexrep(link.link_id)}", RNS.LOG_VERBOSE)
RNS.log(f"Establishment timeout is {RNS.prettytime(link.establishment_timeout)} for incoming link request "+RNS.prettyhexrep(link.link_id), RNS.LOG_EXTREME) RNS.log(f"Establishment timeout is {RNS.prettytime(link.establishment_timeout)} for incoming link request {RNS.prettyhexrep(link.link_id)}", RNS.LOG_EXTREME)
link.handshake() link.handshake()
link.attached_interface = packet.receiving_interface link.attached_interface = packet.receiving_interface
link.prove() link.prove()
@ -125,12 +125,12 @@ class Link:
link.last_inbound = time.time() link.last_inbound = time.time()
link.start_watchdog() link.start_watchdog()
RNS.log("Incoming link request "+str(link)+" accepted on "+str(link.attached_interface), RNS.LOG_DEBUG) RNS.log(f"Incoming link request {link} accepted on {link.attached_interface}", RNS.LOG_DEBUG)
return link return link
except Exception as e: except Exception as e:
RNS.log("Validating link request failed", RNS.LOG_VERBOSE) RNS.log("Validating link request failed", RNS.LOG_VERBOSE)
RNS.log("exc: "+str(e)) RNS.log(f"exc: {e}")
return None return None
else: else:
@ -219,8 +219,8 @@ class Link:
self.start_watchdog() self.start_watchdog()
self.packet.send() self.packet.send()
self.had_outbound() self.had_outbound()
RNS.log("Link request "+RNS.prettyhexrep(self.link_id)+" sent to "+str(self.destination), RNS.LOG_DEBUG) RNS.log(f"Link request {RNS.prettyhexrep(self.link_id)} sent to {self.destination}", RNS.LOG_DEBUG)
RNS.log(f"Establishment timeout is {RNS.prettytime(self.establishment_timeout)} for link request "+RNS.prettyhexrep(self.link_id), RNS.LOG_EXTREME) RNS.log(f"Establishment timeout is {RNS.prettytime(self.establishment_timeout)} for link request {RNS.prettyhexrep(self.link_id)}", RNS.LOG_EXTREME)
def load_peer(self, peer_pub_bytes, peer_sig_pub_bytes): def load_peer(self, peer_pub_bytes, peer_sig_pub_bytes):
@ -249,7 +249,7 @@ class Link:
context=self.get_context(), context=self.get_context(),
) )
else: else:
RNS.log("Handshake attempt on "+str(self)+" with invalid state "+str(self.status), RNS.LOG_ERROR) RNS.log(f"Handshake attempt on {self} with invalid state {self.status}", RNS.LOG_ERROR)
def prove(self): def prove(self):
@ -291,7 +291,7 @@ class Link:
if self.destination.identity.validate(signature, signed_data): if self.destination.identity.validate(signature, signed_data):
if self.status != Link.HANDSHAKE: if self.status != Link.HANDSHAKE:
raise IOError("Invalid link state for proof validation: "+str(self.status)) raise OSError(f"Invalid link state for proof validation: {self.status}")
self.rtt = time.time() - self.request_time self.rtt = time.time() - self.request_time
self.attached_interface = packet.receiving_interface self.attached_interface = packet.receiving_interface
@ -300,7 +300,7 @@ class Link:
self.activated_at = time.time() self.activated_at = time.time()
self.last_proof = self.activated_at self.last_proof = self.activated_at
RNS.Transport.activate_link(self) RNS.Transport.activate_link(self)
RNS.log("Link "+str(self)+" established with "+str(self.destination)+", RTT is "+str(round(self.rtt, 3))+"s", RNS.LOG_VERBOSE) RNS.log(f"Link {self} established with {self.destination}, RTT is {round(self.rtt, 3)}s", RNS.LOG_VERBOSE)
if self.rtt != None and self.establishment_cost != None and self.rtt > 0 and self.establishment_cost > 0: if self.rtt != None and self.establishment_cost != None and self.rtt > 0 and self.establishment_cost > 0:
self.establishment_rate = self.establishment_cost/self.rtt self.establishment_rate = self.establishment_cost/self.rtt
@ -315,12 +315,12 @@ class Link:
thread.daemon = True thread.daemon = True
thread.start() thread.start()
else: else:
RNS.log("Invalid link proof signature received by "+str(self)+". Ignoring.", RNS.LOG_DEBUG) RNS.log(f"Invalid link proof signature received by {self}. Ignoring.", RNS.LOG_DEBUG)
except Exception as e: except Exception as e:
self.status = Link.CLOSED self.status = Link.CLOSED
RNS.log("An error ocurred while validating link request proof on "+str(self)+".", RNS.LOG_ERROR) RNS.log(f"An error ocurred while validating link request proof on {self}.", RNS.LOG_ERROR)
RNS.log("The contained exception was: "+str(e), RNS.LOG_ERROR) RNS.log(f"The contained exception was: {e}", RNS.LOG_ERROR)
def identify(self, identity): def identify(self, identity):
@ -380,7 +380,7 @@ class Link:
else: else:
request_id = RNS.Identity.truncated_hash(packed_request) request_id = RNS.Identity.truncated_hash(packed_request)
RNS.log("Sending request "+RNS.prettyhexrep(request_id)+" as resource.", RNS.LOG_DEBUG) RNS.log(f"Sending request {RNS.prettyhexrep(request_id)} as resource.", RNS.LOG_DEBUG)
request_resource = RNS.Resource(packed_request, self, request_id = request_id, is_response = False, timeout = timeout) request_resource = RNS.Resource(packed_request, self, request_id = request_id, is_response = False, timeout = timeout)
return RequestReceipt( return RequestReceipt(
@ -411,10 +411,10 @@ class Link:
if self.owner.callbacks.link_established != None: if self.owner.callbacks.link_established != None:
self.owner.callbacks.link_established(self) self.owner.callbacks.link_established(self)
except Exception as e: except Exception as e:
RNS.log("Error occurred in external link establishment callback. The contained exception was: "+str(e), RNS.LOG_ERROR) RNS.log(f"Error occurred in external link establishment callback. The contained exception was: {e}", RNS.LOG_ERROR)
except Exception as e: except Exception as e:
RNS.log("Error occurred while processing RTT packet, tearing down link. The contained exception was: "+str(e), RNS.LOG_ERROR) RNS.log(f"Error occurred while processing RTT packet, tearing down link. The contained exception was: {e}", RNS.LOG_ERROR)
self.teardown() self.teardown()
def track_phy_stats(self, track): def track_phy_stats(self, track):
@ -563,7 +563,7 @@ class Link:
try: try:
self.callbacks.link_closed(self) self.callbacks.link_closed(self)
except Exception as e: except Exception as e:
RNS.log("Error while executing link closed callback from "+str(self)+". The contained exception was: "+str(e), RNS.LOG_ERROR) RNS.log(f"Error while executing link closed callback from {self}. The contained exception was: {e}", RNS.LOG_ERROR)
def start_watchdog(self): def start_watchdog(self):
@ -634,7 +634,7 @@ class Link:
if sleep_time == 0: if sleep_time == 0:
RNS.log("Warning! Link watchdog sleep time of 0!", RNS.LOG_ERROR) RNS.log("Warning! Link watchdog sleep time of 0!", RNS.LOG_ERROR)
if sleep_time == None or sleep_time < 0: if sleep_time == None or sleep_time < 0:
RNS.log("Timing error! Tearing down link "+str(self)+" now.", RNS.LOG_ERROR) RNS.log(f"Timing error! Tearing down link {self} now.", RNS.LOG_ERROR)
self.teardown() self.teardown()
sleep_time = 0.1 sleep_time = 0.1
@ -683,7 +683,7 @@ class Link:
allowed = True allowed = True
if allowed: if allowed:
RNS.log("Handling request "+RNS.prettyhexrep(request_id)+" for: "+str(path), RNS.LOG_DEBUG) RNS.log(f"Handling request {RNS.prettyhexrep(request_id)} for: {path}", RNS.LOG_DEBUG)
if len(inspect.signature(response_generator).parameters) == 5: if len(inspect.signature(response_generator).parameters) == 5:
response = response_generator(path, request_data, request_id, self.__remote_identity, requested_at) response = response_generator(path, request_data, request_id, self.__remote_identity, requested_at)
elif len(inspect.signature(response_generator).parameters) == 6: elif len(inspect.signature(response_generator).parameters) == 6:
@ -700,7 +700,7 @@ class Link:
response_resource = RNS.Resource(packed_response, self, request_id = request_id, is_response = True) response_resource = RNS.Resource(packed_response, self, request_id = request_id, is_response = True)
else: else:
identity_string = str(self.get_remote_identity()) if self.get_remote_identity() != None else "<Unknown>" identity_string = str(self.get_remote_identity()) if self.get_remote_identity() != None else "<Unknown>"
RNS.log("Request "+RNS.prettyhexrep(request_id)+" from "+identity_string+" not allowed for: "+str(path), RNS.LOG_DEBUG) RNS.log(f"Request {RNS.prettyhexrep(request_id)} from {identity_string} not allowed for: {path}", RNS.LOG_DEBUG)
def handle_response(self, request_id, response_data, response_size, response_transfer_size): def handle_response(self, request_id, response_data, response_size, response_transfer_size):
if self.status == Link.ACTIVE: if self.status == Link.ACTIVE:
@ -715,7 +715,7 @@ class Link:
pending_request.response_transfer_size += response_transfer_size pending_request.response_transfer_size += response_transfer_size
pending_request.response_received(response_data) pending_request.response_received(response_data)
except Exception as e: except Exception as e:
RNS.log("Error occurred while handling response. The contained exception was: "+str(e), RNS.LOG_ERROR) RNS.log(f"Error occurred while handling response. The contained exception was: {e}", RNS.LOG_ERROR)
break break
@ -732,7 +732,7 @@ class Link:
self.handle_request(request_id, request_data) self.handle_request(request_id, request_data)
else: else:
RNS.log("Incoming request resource failed with status: "+RNS.hexrep([resource.status]), RNS.LOG_DEBUG) RNS.log(f"Incoming request resource failed with status: {RNS.hexrep([resource.status])}", RNS.LOG_DEBUG)
def response_resource_concluded(self, resource): def response_resource_concluded(self, resource):
if resource.status == RNS.Resource.COMPLETE: if resource.status == RNS.Resource.COMPLETE:
@ -743,7 +743,7 @@ class Link:
self.handle_response(request_id, response_data, resource.total_size, resource.size) self.handle_response(request_id, response_data, resource.total_size, resource.size)
else: else:
RNS.log("Incoming response resource failed with status: "+RNS.hexrep([resource.status]), RNS.LOG_DEBUG) RNS.log(f"Incoming response resource failed with status: {RNS.hexrep([resource.status])}", RNS.LOG_DEBUG)
for pending_request in self.pending_requests: for pending_request in self.pending_requests:
if pending_request.request_id == resource.request_id: if pending_request.request_id == resource.request_id:
pending_request.request_timed_out(None) pending_request.request_timed_out(None)
@ -794,7 +794,7 @@ class Link:
packet.prove() packet.prove()
should_query = True should_query = True
except Exception as e: except Exception as e:
RNS.log("Error while executing proof request callback from "+str(self)+". The contained exception was: "+str(e), RNS.LOG_ERROR) RNS.log(f"Error while executing proof request callback from {self}. The contained exception was: {e}", RNS.LOG_ERROR)
self.__update_phy_stats(packet, query_shared=should_query) self.__update_phy_stats(packet, query_shared=should_query)
@ -814,7 +814,7 @@ class Link:
try: try:
self.callbacks.remote_identified(self, self.__remote_identity) self.callbacks.remote_identified(self, self.__remote_identity)
except Exception as e: except Exception as e:
RNS.log("Error while executing remote identified callback from "+str(self)+". The contained exception was: "+str(e), RNS.LOG_ERROR) RNS.log(f"Error while executing remote identified callback from {self}. The contained exception was: {e}", RNS.LOG_ERROR)
self.__update_phy_stats(packet, query_shared=True) self.__update_phy_stats(packet, query_shared=True)
@ -827,7 +827,7 @@ class Link:
self.handle_request(request_id, unpacked_request) self.handle_request(request_id, unpacked_request)
self.__update_phy_stats(packet, query_shared=True) self.__update_phy_stats(packet, query_shared=True)
except Exception as e: except Exception as e:
RNS.log("Error occurred while handling request. The contained exception was: "+str(e), RNS.LOG_ERROR) RNS.log(f"Error occurred while handling request. The contained exception was: {e}", RNS.LOG_ERROR)
elif packet.context == RNS.Packet.RESPONSE: elif packet.context == RNS.Packet.RESPONSE:
try: try:
@ -840,7 +840,7 @@ class Link:
self.handle_response(request_id, response_data, transfer_size, transfer_size) self.handle_response(request_id, response_data, transfer_size, transfer_size)
self.__update_phy_stats(packet, query_shared=True) self.__update_phy_stats(packet, query_shared=True)
except Exception as e: except Exception as e:
RNS.log("Error occurred while handling response. The contained exception was: "+str(e), RNS.LOG_ERROR) RNS.log(f"Error occurred while handling response. The contained exception was: {e}", RNS.LOG_ERROR)
elif packet.context == RNS.Packet.LRRTT: elif packet.context == RNS.Packet.LRRTT:
if not self.initiator: if not self.initiator:
@ -883,7 +883,7 @@ class Link:
if self.callbacks.resource(resource_advertisement): if self.callbacks.resource(resource_advertisement):
RNS.Resource.accept(packet, self.callbacks.resource_concluded) RNS.Resource.accept(packet, self.callbacks.resource_concluded)
except Exception as e: except Exception as e:
RNS.log("Error while executing resource accept callback from "+str(self)+". The contained exception was: "+str(e), RNS.LOG_ERROR) RNS.log(f"Error while executing resource accept callback from {self}. The contained exception was: {e}", RNS.LOG_ERROR)
elif self.resource_strategy == Link.ACCEPT_ALL: elif self.resource_strategy == Link.ACCEPT_ALL:
RNS.Resource.accept(packet, self.callbacks.resource_concluded) RNS.Resource.accept(packet, self.callbacks.resource_concluded)
@ -970,13 +970,13 @@ class Link:
try: try:
self.fernet = Fernet(self.derived_key) self.fernet = Fernet(self.derived_key)
except Exception as e: except Exception as e:
RNS.log("Could not instantiate Fernet while performin encryption on link "+str(self)+". The contained exception was: "+str(e), RNS.LOG_ERROR) RNS.log(f"Could not instantiate Fernet while performin encryption on link {self}. The contained exception was: {e}", RNS.LOG_ERROR)
raise e raise e
return self.fernet.encrypt(plaintext) return self.fernet.encrypt(plaintext)
except Exception as e: except Exception as e:
RNS.log("Encryption on link "+str(self)+" failed. The contained exception was: "+str(e), RNS.LOG_ERROR) RNS.log(f"Encryption on link {self} failed. The contained exception was: {e}", RNS.LOG_ERROR)
raise e raise e
@ -988,7 +988,7 @@ class Link:
return self.fernet.decrypt(ciphertext) return self.fernet.decrypt(ciphertext)
except Exception as e: except Exception as e:
RNS.log("Decryption failed on link "+str(self)+". The contained exception was: "+str(e), RNS.LOG_ERROR) RNS.log(f"Decryption failed on link {self}. The contained exception was: {e}", RNS.LOG_ERROR)
return None return None
@ -1169,7 +1169,7 @@ class RequestReceipt():
def request_resource_concluded(self, resource): def request_resource_concluded(self, resource):
if resource.status == RNS.Resource.COMPLETE: if resource.status == RNS.Resource.COMPLETE:
RNS.log("Request "+RNS.prettyhexrep(self.request_id)+" successfully sent as resource.", RNS.LOG_DEBUG) RNS.log(f"Request {RNS.prettyhexrep(self.request_id)} successfully sent as resource.", RNS.LOG_DEBUG)
if self.started_at == None: if self.started_at == None:
self.started_at = time.time() self.started_at = time.time()
self.status = RequestReceipt.DELIVERED self.status = RequestReceipt.DELIVERED
@ -1178,7 +1178,7 @@ class RequestReceipt():
response_timeout_thread.daemon = True response_timeout_thread.daemon = True
response_timeout_thread.start() response_timeout_thread.start()
else: else:
RNS.log("Sending request "+RNS.prettyhexrep(self.request_id)+" as resource failed with status: "+RNS.hexrep([resource.status]), RNS.LOG_DEBUG) RNS.log(f"Sending request {RNS.prettyhexrep(self.request_id)} as resource failed with status: {RNS.hexrep([resource.status])}", RNS.LOG_DEBUG)
self.status = RequestReceipt.FAILED self.status = RequestReceipt.FAILED
self.concluded_at = time.time() self.concluded_at = time.time()
self.link.pending_requests.remove(self) self.link.pending_requests.remove(self)
@ -1187,7 +1187,7 @@ class RequestReceipt():
try: try:
self.callbacks.failed(self) self.callbacks.failed(self)
except Exception as e: except Exception as e:
RNS.log("Error while executing request failed callback from "+str(self)+". The contained exception was: "+str(e), RNS.LOG_ERROR) RNS.log(f"Error while executing request failed callback from {self}. The contained exception was: {e}", RNS.LOG_ERROR)
def __response_timeout_job(self): def __response_timeout_job(self):
@ -1208,7 +1208,7 @@ class RequestReceipt():
try: try:
self.callbacks.failed(self) self.callbacks.failed(self)
except Exception as e: except Exception as e:
RNS.log("Error while executing request timed out callback from "+str(self)+". The contained exception was: "+str(e), RNS.LOG_ERROR) RNS.log(f"Error while executing request timed out callback from {self}. The contained exception was: {e}", RNS.LOG_ERROR)
def response_resource_progress(self, resource): def response_resource_progress(self, resource):
@ -1229,7 +1229,7 @@ class RequestReceipt():
try: try:
self.callbacks.progress(self) self.callbacks.progress(self)
except Exception as e: except Exception as e:
RNS.log("Error while executing response progress callback from "+str(self)+". The contained exception was: "+str(e), RNS.LOG_ERROR) RNS.log(f"Error while executing response progress callback from {self}. The contained exception was: {e}", RNS.LOG_ERROR)
else: else:
resource.cancel() resource.cancel()
@ -1252,13 +1252,13 @@ class RequestReceipt():
try: try:
self.callbacks.progress(self) self.callbacks.progress(self)
except Exception as e: except Exception as e:
RNS.log("Error while executing response progress callback from "+str(self)+". The contained exception was: "+str(e), RNS.LOG_ERROR) RNS.log(f"Error while executing response progress callback from {self}. The contained exception was: {e}", RNS.LOG_ERROR)
if self.callbacks.response != None: if self.callbacks.response != None:
try: try:
self.callbacks.response(self) self.callbacks.response(self)
except Exception as e: except Exception as e:
RNS.log("Error while executing response received callback from "+str(self)+". The contained exception was: "+str(e), RNS.LOG_ERROR) RNS.log(f"Error while executing response received callback from {self}. The contained exception was: {e}", RNS.LOG_ERROR)
def get_request_id(self): def get_request_id(self):
""" """

View File

@ -208,14 +208,14 @@ class Packet:
# Announce packets are not encrypted # Announce packets are not encrypted
self.ciphertext = self.data self.ciphertext = self.data
else: else:
raise IOError("Packet with header type 2 must have a transport ID") raise OSError("Packet with header type 2 must have a transport ID")
self.header += bytes([self.context]) self.header += bytes([self.context])
self.raw = self.header + self.ciphertext self.raw = self.header + self.ciphertext
if len(self.raw) > self.MTU: if len(self.raw) > self.MTU:
raise IOError("Packet size of "+str(len(self.raw))+" exceeds MTU of "+str(self.MTU)+" bytes") raise OSError(f"Packet size of {len(self.raw)} exceeds MTU of {self.MTU} bytes")
self.packed = True self.packed = True
self.update_hash() self.update_hash()
@ -250,7 +250,7 @@ class Packet:
return True return True
except Exception as e: except Exception as e:
RNS.log("Received malformed packet, dropping it. The contained exception was: "+str(e), RNS.LOG_EXTREME) RNS.log(f"Received malformed packet, dropping it. The contained exception was: {e}", RNS.LOG_EXTREME)
return False return False
def send(self): def send(self):
@ -262,7 +262,7 @@ class Packet:
if not self.sent: if not self.sent:
if self.destination.type == RNS.Destination.LINK: if self.destination.type == RNS.Destination.LINK:
if self.destination.status == RNS.Link.CLOSED: if self.destination.status == RNS.Link.CLOSED:
raise IOError("Attempt to transmit over a closed link") raise OSError("Attempt to transmit over a closed link")
else: else:
self.destination.last_outbound = time.time() self.destination.last_outbound = time.time()
self.destination.tx += 1 self.destination.tx += 1
@ -280,7 +280,7 @@ class Packet:
return False return False
else: else:
raise IOError("Packet was already sent") raise OSError("Packet was already sent")
def resend(self): def resend(self):
""" """
@ -301,7 +301,7 @@ class Packet:
self.receipt = None self.receipt = None
return False return False
else: else:
raise IOError("Packet was not sent yet") raise OSError("Packet was not sent yet")
def prove(self, destination=None): def prove(self, destination=None):
if self.fromPacked and hasattr(self, "destination") and self.destination: if self.fromPacked and hasattr(self, "destination") and self.destination:
@ -419,8 +419,8 @@ class PacketReceipt:
try: try:
self.callbacks.delivery(self) self.callbacks.delivery(self)
except Exception as e: except Exception as e:
RNS.log("An error occurred while evaluating external delivery callback for "+str(link), RNS.LOG_ERROR) RNS.log(f"An error occurred while evaluating external delivery callback for {link}", RNS.LOG_ERROR)
RNS.log("The contained exception was: "+str(e), RNS.LOG_ERROR) RNS.log(f"The contained exception was: {e}", RNS.LOG_ERROR)
RNS.trace_exception(e) RNS.trace_exception(e)
return True return True
@ -465,7 +465,7 @@ class PacketReceipt:
try: try:
self.callbacks.delivery(self) self.callbacks.delivery(self)
except Exception as e: except Exception as e:
RNS.log("Error while executing proof validated callback. The contained exception was: "+str(e), RNS.LOG_ERROR) RNS.log(f"Error while executing proof validated callback. The contained exception was: {e}", RNS.LOG_ERROR)
return True return True
else: else:
@ -489,7 +489,7 @@ class PacketReceipt:
try: try:
self.callbacks.delivery(self) self.callbacks.delivery(self)
except Exception as e: except Exception as e:
RNS.log("Error while executing proof validated callback. The contained exception was: "+str(e), RNS.LOG_ERROR) RNS.log(f"Error while executing proof validated callback. The contained exception was: {e}", RNS.LOG_ERROR)
return True return True
else: else:

View File

@ -118,6 +118,7 @@ class Resource:
PART_TIMEOUT_FACTOR = 4 PART_TIMEOUT_FACTOR = 4
PART_TIMEOUT_FACTOR_AFTER_RTT = 2 PART_TIMEOUT_FACTOR_AFTER_RTT = 2
PROOF_TIMEOUT_FACTOR = 3
MAX_RETRIES = 16 MAX_RETRIES = 16
MAX_ADV_RETRIES = 4 MAX_ADV_RETRIES = 4
SENDER_GRACE_TIME = 10.0 SENDER_GRACE_TIME = 10.0
@ -172,7 +173,7 @@ class Resource:
resource.window_flexibility = Resource.WINDOW_FLEXIBILITY resource.window_flexibility = Resource.WINDOW_FLEXIBILITY
resource.last_activity = time.time() resource.last_activity = time.time()
resource.storagepath = RNS.Reticulum.resourcepath+"/"+resource.original_hash.hex() resource.storagepath = f"{RNS.Reticulum.resourcepath}/{resource.original_hash.hex()}"
resource.segment_index = adv.i resource.segment_index = adv.i
resource.total_segments = adv.l resource.total_segments = adv.l
if adv.l > 1: if adv.l > 1:
@ -194,7 +195,7 @@ class Resource:
try: try:
resource.link.callbacks.resource_started(resource) resource.link.callbacks.resource_started(resource)
except Exception as e: except Exception as e:
RNS.log("Error while executing resource started callback from "+str(resource)+". The contained exception was: "+str(e), RNS.LOG_ERROR) RNS.log(f"Error while executing resource started callback from {resource}. The contained exception was: {e}", RNS.LOG_ERROR)
resource.hashmap_update(0, resource.hashmap_raw) resource.hashmap_update(0, resource.hashmap_raw)
@ -203,7 +204,7 @@ class Resource:
return resource return resource
else: else:
RNS.log("Ignoring resource advertisement for "+RNS.prettyhexrep(resource.hash)+", resource already transferring", RNS.LOG_DEBUG) RNS.log(f"Ignoring resource advertisement for {RNS.prettyhexrep(resource.hash)}, resource already transferring", RNS.LOG_DEBUG)
return None return None
except Exception as e: except Exception as e:
@ -307,7 +308,7 @@ class Resource:
if (auto_compress and len(self.uncompressed_data) <= Resource.AUTO_COMPRESS_MAX_SIZE): if (auto_compress and len(self.uncompressed_data) <= Resource.AUTO_COMPRESS_MAX_SIZE):
RNS.log("Compressing resource data...", RNS.LOG_DEBUG) RNS.log("Compressing resource data...", RNS.LOG_DEBUG)
self.compressed_data = bz2.compress(self.uncompressed_data) self.compressed_data = bz2.compress(self.uncompressed_data)
RNS.log("Compression completed in "+str(round(time.time()-compression_began, 3))+" seconds", RNS.LOG_DEBUG) RNS.log(f"Compression completed in {round(time.time() - compression_began, 3)} seconds", RNS.LOG_DEBUG)
else: else:
self.compressed_data = self.uncompressed_data self.compressed_data = self.uncompressed_data
@ -316,7 +317,7 @@ class Resource:
if (self.compressed_size < self.uncompressed_size and auto_compress): if (self.compressed_size < self.uncompressed_size and auto_compress):
saved_bytes = len(self.uncompressed_data) - len(self.compressed_data) saved_bytes = len(self.uncompressed_data) - len(self.compressed_data)
RNS.log("Compression saved "+str(saved_bytes)+" bytes, sending compressed", RNS.LOG_DEBUG) RNS.log(f"Compression saved {saved_bytes} bytes, sending compressed", RNS.LOG_DEBUG)
self.data = b"" self.data = b""
self.data += RNS.Identity.get_random_hash()[:Resource.RANDOM_HASH_SIZE] self.data += RNS.Identity.get_random_hash()[:Resource.RANDOM_HASH_SIZE]
@ -351,7 +352,7 @@ class Resource:
hashmap_ok = False hashmap_ok = False
while not hashmap_ok: while not hashmap_ok:
hashmap_computation_began = time.time() hashmap_computation_began = time.time()
RNS.log("Starting resource hashmap computation with "+str(hashmap_entries)+" entries...", RNS.LOG_DEBUG) RNS.log(f"Starting resource hashmap computation with {hashmap_entries} entries...", RNS.LOG_DEBUG)
self.random_hash = RNS.Identity.get_random_hash()[:Resource.RANDOM_HASH_SIZE] self.random_hash = RNS.Identity.get_random_hash()[:Resource.RANDOM_HASH_SIZE]
self.hash = RNS.Identity.full_hash(data+self.random_hash) self.hash = RNS.Identity.full_hash(data+self.random_hash)
@ -387,7 +388,7 @@ class Resource:
self.hashmap += part.map_hash self.hashmap += part.map_hash
self.parts.append(part) self.parts.append(part)
RNS.log("Hashmap computation concluded in "+str(round(time.time()-hashmap_computation_began, 3))+" seconds", RNS.LOG_DEBUG) RNS.log(f"Hashmap computation concluded in {round(time.time() - hashmap_computation_began, 3)} seconds", RNS.LOG_DEBUG)
if advertise: if advertise:
self.advertise() self.advertise()
@ -446,9 +447,9 @@ class Resource:
self.status = Resource.ADVERTISED self.status = Resource.ADVERTISED
self.retries_left = self.max_adv_retries self.retries_left = self.max_adv_retries
self.link.register_outgoing_resource(self) self.link.register_outgoing_resource(self)
RNS.log("Sent resource advertisement for "+RNS.prettyhexrep(self.hash), RNS.LOG_DEBUG) RNS.log(f"Sent resource advertisement for {RNS.prettyhexrep(self.hash)}", RNS.LOG_DEBUG)
except Exception as e: except Exception as e:
RNS.log("Could not advertise resource, the contained exception was: "+str(e), RNS.LOG_ERROR) RNS.log(f"Could not advertise resource, the contained exception was: {e}", RNS.LOG_ERROR)
self.cancel() self.cancel()
return return
@ -486,7 +487,7 @@ class Resource:
self.adv_sent = self.last_activity self.adv_sent = self.last_activity
sleep_time = 0.001 sleep_time = 0.001
except Exception as e: except Exception as e:
RNS.log("Could not resend advertisement packet, cancelling resource. The contained exception was: "+str(e), RNS.LOG_VERBOSE) RNS.log(f"Could not resend advertisement packet, cancelling resource. The contained exception was: {e}", RNS.LOG_VERBOSE)
self.cancel() self.cancel()
@ -507,7 +508,7 @@ class Resource:
if sleep_time < 0: if sleep_time < 0:
if self.retries_left > 0: if self.retries_left > 0:
ms = "" if self.outstanding_parts == 1 else "s" ms = "" if self.outstanding_parts == 1 else "s"
RNS.log("Timed out waiting for "+str(self.outstanding_parts)+" part"+ms+", requesting retry", RNS.LOG_DEBUG) RNS.log(f"Timed out waiting for {self.outstanding_parts} part{ms}, requesting retry", RNS.LOG_DEBUG)
if self.window > self.window_min: if self.window > self.window_min:
self.window -= 1 self.window -= 1
if self.window_max > self.window_min: if self.window_max > self.window_min:
@ -532,6 +533,10 @@ class Resource:
sleep_time = 0.001 sleep_time = 0.001
elif self.status == Resource.AWAITING_PROOF: elif self.status == Resource.AWAITING_PROOF:
# Decrease timeout factor since proof packets are
# significantly smaller than full req/resp roundtrip
self.timeout_factor = Resource.PROOF_TIMEOUT_FACTOR
sleep_time = self.last_part_sent + (self.rtt*self.timeout_factor+self.sender_grace_time) - time.time() sleep_time = self.last_part_sent + (self.rtt*self.timeout_factor+self.sender_grace_time) - time.time()
if sleep_time < 0: if sleep_time < 0:
if self.retries_left <= 0: if self.retries_left <= 0:
@ -590,7 +595,7 @@ class Resource:
except Exception as e: except Exception as e:
RNS.log("Error while assembling received resource.", RNS.LOG_ERROR) RNS.log("Error while assembling received resource.", RNS.LOG_ERROR)
RNS.log("The contained exception was: "+str(e), RNS.LOG_ERROR) RNS.log(f"The contained exception was: {e}", RNS.LOG_ERROR)
self.status = Resource.CORRUPT self.status = Resource.CORRUPT
self.link.resource_concluded(self) self.link.resource_concluded(self)
@ -601,7 +606,7 @@ class Resource:
try: try:
self.callback(self) self.callback(self)
except Exception as e: except Exception as e:
RNS.log("Error while executing resource assembled callback from "+str(self)+". The contained exception was: "+str(e), RNS.LOG_ERROR) RNS.log(f"Error while executing resource assembled callback from {self}. The contained exception was: {e}", RNS.LOG_ERROR)
try: try:
if hasattr(self.data, "close") and callable(self.data.close): if hasattr(self.data, "close") and callable(self.data.close):
@ -613,7 +618,7 @@ class Resource:
RNS.log("Error while cleaning up resource files, the contained exception was:", RNS.LOG_ERROR) RNS.log("Error while cleaning up resource files, the contained exception was:", RNS.LOG_ERROR)
RNS.log(str(e)) RNS.log(str(e))
else: else:
RNS.log("Resource segment "+str(self.segment_index)+" of "+str(self.total_segments)+" received, waiting for next segment to be announced", RNS.LOG_DEBUG) RNS.log(f"Resource segment {self.segment_index} of {self.total_segments} received, waiting for next segment to be announced", RNS.LOG_DEBUG)
def prove(self): def prove(self):
@ -623,9 +628,10 @@ class Resource:
proof_data = self.hash+proof proof_data = self.hash+proof
proof_packet = RNS.Packet(self.link, proof_data, packet_type=RNS.Packet.PROOF, context=RNS.Packet.RESOURCE_PRF) proof_packet = RNS.Packet(self.link, proof_data, packet_type=RNS.Packet.PROOF, context=RNS.Packet.RESOURCE_PRF)
proof_packet.send() proof_packet.send()
RNS.Transport.cache(proof_packet, force_cache=True)
except Exception as e: except Exception as e:
RNS.log("Could not send proof packet, cancelling resource", RNS.LOG_DEBUG) RNS.log("Could not send proof packet, cancelling resource", RNS.LOG_DEBUG)
RNS.log("The contained exception was: "+str(e), RNS.LOG_DEBUG) RNS.log(f"The contained exception was: {e}", RNS.LOG_DEBUG)
self.cancel() self.cancel()
def __prepare_next_segment(self): def __prepare_next_segment(self):
@ -656,7 +662,7 @@ class Resource:
try: try:
self.callback(self) self.callback(self)
except Exception as e: except Exception as e:
RNS.log("Error while executing resource concluded callback from "+str(self)+". The contained exception was: "+str(e), RNS.LOG_ERROR) RNS.log(f"Error while executing resource concluded callback from {self}. The contained exception was: {e}", RNS.LOG_ERROR)
finally: finally:
try: try:
if hasattr(self, "input_file"): if hasattr(self, "input_file"):
@ -664,7 +670,7 @@ class Resource:
self.input_file.close() self.input_file.close()
except Exception as e: except Exception as e:
RNS.log("Error while closing resource input file: "+str(e), RNS.LOG_ERROR) RNS.log(f"Error while closing resource input file: {e}", RNS.LOG_ERROR)
else: else:
# Otherwise we'll recursively create the # Otherwise we'll recursively create the
# next segment of the resource # next segment of the resource
@ -741,7 +747,7 @@ class Resource:
try: try:
self.__progress_callback(self) self.__progress_callback(self)
except Exception as e: except Exception as e:
RNS.log("Error while executing progress callback from "+str(self)+". The contained exception was: "+str(e), RNS.LOG_ERROR) RNS.log(f"Error while executing progress callback from {self}. The contained exception was: {e}", RNS.LOG_ERROR)
i += 1 i += 1
@ -829,7 +835,7 @@ class Resource:
except Exception as e: except Exception as e:
RNS.log("Could not send resource request packet, cancelling resource", RNS.LOG_DEBUG) RNS.log("Could not send resource request packet, cancelling resource", RNS.LOG_DEBUG)
RNS.log("The contained exception was: "+str(e), RNS.LOG_DEBUG) RNS.log(f"The contained exception was: {e}", RNS.LOG_DEBUG)
self.cancel() self.cancel()
# Called on outgoing resource to make it send more data # Called on outgoing resource to make it send more data
@ -875,7 +881,7 @@ class Resource:
except Exception as e: except Exception as e:
RNS.log("Resource could not send parts, cancelling transfer!", RNS.LOG_DEBUG) RNS.log("Resource could not send parts, cancelling transfer!", RNS.LOG_DEBUG)
RNS.log("The contained exception was: "+str(e), RNS.LOG_DEBUG) RNS.log(f"The contained exception was: {e}", RNS.LOG_DEBUG)
self.cancel() self.cancel()
if wants_more_hashmap: if wants_more_hashmap:
@ -914,17 +920,18 @@ class Resource:
self.last_activity = time.time() self.last_activity = time.time()
except Exception as e: except Exception as e:
RNS.log("Could not send resource HMU packet, cancelling resource", RNS.LOG_DEBUG) RNS.log("Could not send resource HMU packet, cancelling resource", RNS.LOG_DEBUG)
RNS.log("The contained exception was: "+str(e), RNS.LOG_DEBUG) RNS.log(f"The contained exception was: {e}", RNS.LOG_DEBUG)
self.cancel() self.cancel()
if self.sent_parts == len(self.parts): if self.sent_parts == len(self.parts):
self.status = Resource.AWAITING_PROOF self.status = Resource.AWAITING_PROOF
self.retries_left = 3
if self.__progress_callback != None: if self.__progress_callback != None:
try: try:
self.__progress_callback(self) self.__progress_callback(self)
except Exception as e: except Exception as e:
RNS.log("Error while executing progress callback from "+str(self)+". The contained exception was: "+str(e), RNS.LOG_ERROR) RNS.log(f"Error while executing progress callback from {self}. The contained exception was: {e}", RNS.LOG_ERROR)
def cancel(self): def cancel(self):
""" """
@ -938,7 +945,7 @@ class Resource:
cancel_packet = RNS.Packet(self.link, self.hash, context=RNS.Packet.RESOURCE_ICL) cancel_packet = RNS.Packet(self.link, self.hash, context=RNS.Packet.RESOURCE_ICL)
cancel_packet.send() cancel_packet.send()
except Exception as e: except Exception as e:
RNS.log("Could not send resource cancel packet, the contained exception was: "+str(e), RNS.LOG_ERROR) RNS.log(f"Could not send resource cancel packet, the contained exception was: {e}", RNS.LOG_ERROR)
self.link.cancel_outgoing_resource(self) self.link.cancel_outgoing_resource(self)
else: else:
self.link.cancel_incoming_resource(self) self.link.cancel_incoming_resource(self)
@ -948,7 +955,7 @@ class Resource:
self.link.resource_concluded(self) self.link.resource_concluded(self)
self.callback(self) self.callback(self)
except Exception as e: except Exception as e:
RNS.log("Error while executing callbacks on resource cancel from "+str(self)+". The contained exception was: "+str(e), RNS.LOG_ERROR) RNS.log(f"Error while executing callbacks on resource cancel from {self}. The contained exception was: {e}", RNS.LOG_ERROR)
def set_callback(self, callback): def set_callback(self, callback):
self.callback = callback self.callback = callback
@ -1061,7 +1068,7 @@ class Resource:
return self.compressed return self.compressed
def __str__(self): def __str__(self):
return "<"+RNS.hexrep(self.hash,delimit=False)+"/"+RNS.hexrep(self.link.link_id,delimit=False)+">" return f"<{RNS.hexrep(self.hash, delimit=False)}/{RNS.hexrep(self.link.link_id, delimit=False)}>"
class ResourceAdvertisement: class ResourceAdvertisement:

View File

@ -29,6 +29,7 @@ if get_platform() == "android":
from .Interfaces import TCPInterface from .Interfaces import TCPInterface
from .Interfaces import UDPInterface from .Interfaces import UDPInterface
from .Interfaces import I2PInterface from .Interfaces import I2PInterface
from .Interfaces import RNodeMultiInterface
from .Interfaces.Android import RNodeInterface from .Interfaces.Android import RNodeInterface
from .Interfaces.Android import SerialInterface from .Interfaces.Android import SerialInterface
from .Interfaces.Android import KISSInterface from .Interfaces.Android import KISSInterface
@ -203,20 +204,20 @@ class Reticulum:
else: else:
if os.path.isdir("/etc/reticulum") and os.path.isfile("/etc/reticulum/config"): if os.path.isdir("/etc/reticulum") and os.path.isfile("/etc/reticulum/config"):
Reticulum.configdir = "/etc/reticulum" Reticulum.configdir = "/etc/reticulum"
elif os.path.isdir(Reticulum.userdir+"/.config/reticulum") and os.path.isfile(Reticulum.userdir+"/.config/reticulum/config"): elif os.path.isdir(f"{Reticulum.userdir}/.config/reticulum") and os.path.isfile(f"{Reticulum.userdir}/.config/reticulum/config"):
Reticulum.configdir = Reticulum.userdir+"/.config/reticulum" Reticulum.configdir = f"{Reticulum.userdir}/.config/reticulum"
else: else:
Reticulum.configdir = Reticulum.userdir+"/.reticulum" Reticulum.configdir = f"{Reticulum.userdir}/.reticulum"
if logdest == RNS.LOG_FILE: if logdest == RNS.LOG_FILE:
RNS.logdest = RNS.LOG_FILE RNS.logdest = RNS.LOG_FILE
RNS.logfile = Reticulum.configdir+"/logfile" RNS.logfile = f"{Reticulum.configdir}/logfile"
Reticulum.configpath = Reticulum.configdir+"/config" Reticulum.configpath = f"{Reticulum.configdir}/config"
Reticulum.storagepath = Reticulum.configdir+"/storage" Reticulum.storagepath = f"{Reticulum.configdir}/storage"
Reticulum.cachepath = Reticulum.configdir+"/storage/cache" Reticulum.cachepath = f"{Reticulum.configdir}/storage/cache"
Reticulum.resourcepath = Reticulum.configdir+"/storage/resources" Reticulum.resourcepath = f"{Reticulum.configdir}/storage/resources"
Reticulum.identitypath = Reticulum.configdir+"/storage/identities" Reticulum.identitypath = f"{Reticulum.configdir}/storage/identities"
Reticulum.__transport_enabled = False Reticulum.__transport_enabled = False
Reticulum.__remote_management_enabled = False Reticulum.__remote_management_enabled = False
@ -266,17 +267,17 @@ class Reticulum:
try: try:
self.config = ConfigObj(self.configpath) self.config = ConfigObj(self.configpath)
except Exception as e: except Exception as e:
RNS.log("Could not parse the configuration at "+self.configpath, RNS.LOG_ERROR) RNS.log(f"Could not parse the configuration at {self.configpath}", RNS.LOG_ERROR)
RNS.log("Check your configuration file for errors!", RNS.LOG_ERROR) RNS.log("Check your configuration file for errors!", RNS.LOG_ERROR)
RNS.panic() RNS.panic()
else: else:
RNS.log("Could not load config file, creating default configuration file...") RNS.log("Could not load config file, creating default configuration file...")
self.__create_default_config() self.__create_default_config()
RNS.log("Default config file created. Make any necessary changes in "+Reticulum.configdir+"/config and restart Reticulum if needed.") RNS.log(f"Default config file created. Make any necessary changes in {Reticulum.configdir}/config and restart Reticulum if needed.")
time.sleep(1.5) time.sleep(1.5)
self.__apply_config() self.__apply_config()
RNS.log("Configuration loaded from "+self.configpath, RNS.LOG_VERBOSE) RNS.log(f"Configuration loaded from {self.configpath}", RNS.LOG_VERBOSE)
RNS.Identity.load_known_destinations() RNS.Identity.load_known_destinations()
@ -331,7 +332,7 @@ class Reticulum:
RNS.Transport.interfaces.append(interface) RNS.Transport.interfaces.append(interface)
self.is_shared_instance = True self.is_shared_instance = True
RNS.log("Started shared instance interface: "+str(interface), RNS.LOG_DEBUG) RNS.log(f"Started shared instance interface: {interface}", RNS.LOG_DEBUG)
self.__start_jobs() self.__start_jobs()
except Exception as e: except Exception as e:
@ -353,10 +354,10 @@ class Reticulum:
Reticulum.__transport_enabled = False Reticulum.__transport_enabled = False
Reticulum.__remote_management_enabled = False Reticulum.__remote_management_enabled = False
Reticulum.__allow_probes = False Reticulum.__allow_probes = False
RNS.log("Connected to locally available Reticulum instance via: "+str(interface), RNS.LOG_DEBUG) RNS.log(f"Connected to locally available Reticulum instance via: {interface}", RNS.LOG_DEBUG)
except Exception as e: except Exception as e:
RNS.log("Local shared instance appears to be running, but it could not be connected", RNS.LOG_ERROR) RNS.log("Local shared instance appears to be running, but it could not be connected", RNS.LOG_ERROR)
RNS.log("The contained exception was: "+str(e), RNS.LOG_ERROR) RNS.log(f"The contained exception was: {e}", RNS.LOG_ERROR)
self.is_shared_instance = False self.is_shared_instance = False
self.is_standalone_instance = True self.is_standalone_instance = True
self.is_connected_to_shared_instance = False self.is_connected_to_shared_instance = False
@ -411,11 +412,11 @@ class Reticulum:
for hexhash in v: for hexhash in v:
dest_len = (RNS.Reticulum.TRUNCATED_HASHLENGTH//8)*2 dest_len = (RNS.Reticulum.TRUNCATED_HASHLENGTH//8)*2
if len(hexhash) != dest_len: if len(hexhash) != dest_len:
raise ValueError("Identity hash length for remote management ACL "+str(hexhash)+" is invalid, must be {hex} hexadecimal characters ({byte} bytes).".format(hex=dest_len, byte=dest_len//2)) raise ValueError(f"Identity hash length for remote management ACL {hexhash} is invalid, must be {dest_len} hexadecimal characters ({dest_len // 2} bytes).")
try: try:
allowed_hash = bytes.fromhex(hexhash) allowed_hash = bytes.fromhex(hexhash)
except Exception as e: except Exception as e:
raise ValueError("Invalid identity hash for remote management ACL: "+str(hexhash)) raise ValueError(f"Invalid identity hash for remote management ACL: {hexhash}")
if not allowed_hash in RNS.Transport.remote_management_allowed: if not allowed_hash in RNS.Transport.remote_management_allowed:
RNS.Transport.remote_management_allowed.append(allowed_hash) RNS.Transport.remote_management_allowed.append(allowed_hash)
@ -658,7 +659,7 @@ class Reticulum:
interface.OUT = True interface.OUT = True
if interface_mode == Interface.Interface.MODE_ACCESS_POINT: if interface_mode == Interface.Interface.MODE_ACCESS_POINT:
RNS.log(str(interface)+" does not support Access Point mode, reverting to default mode: Full", RNS.LOG_WARNING) RNS.log(f"{interface} does not support Access Point mode, reverting to default mode: Full", RNS.LOG_WARNING)
interface_mode = Interface.Interface.MODE_FULL interface_mode = Interface.Interface.MODE_FULL
interface.mode = interface_mode interface.mode = interface_mode
@ -695,7 +696,7 @@ class Reticulum:
interface.OUT = True interface.OUT = True
if interface_mode == Interface.Interface.MODE_ACCESS_POINT: if interface_mode == Interface.Interface.MODE_ACCESS_POINT:
RNS.log(str(interface)+" does not support Access Point mode, reverting to default mode: Full", RNS.LOG_WARNING) RNS.log(f"{interface} does not support Access Point mode, reverting to default mode: Full", RNS.LOG_WARNING)
interface_mode = Interface.Interface.MODE_FULL interface_mode = Interface.Interface.MODE_FULL
interface.mode = interface_mode interface.mode = interface_mode
@ -732,7 +733,7 @@ class Reticulum:
interface.OUT = True interface.OUT = True
if interface_mode == Interface.Interface.MODE_ACCESS_POINT: if interface_mode == Interface.Interface.MODE_ACCESS_POINT:
RNS.log(str(interface)+" does not support Access Point mode, reverting to default mode: Full", RNS.LOG_WARNING) RNS.log(f"{interface} does not support Access Point mode, reverting to default mode: Full", RNS.LOG_WARNING)
interface_mode = Interface.Interface.MODE_FULL interface_mode = Interface.Interface.MODE_FULL
interface.mode = interface_mode interface.mode = interface_mode
@ -1029,17 +1030,17 @@ class Reticulum:
# if no subinterfaces are defined # if no subinterfaces are defined
if count == 0: if count == 0:
raise ValueError("No subinterfaces configured for "+name) raise ValueError(f"No subinterfaces configured for {name}")
# if no subinterfaces are enabled # if no subinterfaces are enabled
elif enabled_count == 0: elif enabled_count == 0:
raise ValueError("No subinterfaces enabled for "+name) raise ValueError(f"No subinterfaces enabled for {name}")
id_interval = int(c["id_interval"]) if "id_interval" in c else None id_interval = int(c["id_interval"]) if "id_interval" in c else None
id_callsign = c["id_callsign"] if "id_callsign" in c else None id_callsign = c["id_callsign"] if "id_callsign" in c else None
port = c["port"] if "port" in c else None port = c["port"] if "port" in c else None
if port == None: if port == None:
raise ValueError("No port specified for "+name) raise ValueError(f"No port specified for {name}")
interface = RNodeMultiInterface.RNodeMultiInterface( interface = RNodeMultiInterface.RNodeMultiInterface(
RNS.Transport, RNS.Transport,
@ -1106,14 +1107,14 @@ class Reticulum:
interface.start() interface.start()
else: else:
RNS.log("Skipping disabled interface \""+name+"\"", RNS.LOG_DEBUG) RNS.log(f"Skipping disabled interface \"{name}\"", RNS.LOG_DEBUG)
except Exception as e: except Exception as e:
RNS.log("The interface \""+name+"\" could not be created. Check your configuration file for errors!", RNS.LOG_ERROR) RNS.log(f"The interface \"{name}\" could not be created. Check your configuration file for errors!", RNS.LOG_ERROR)
RNS.log("The contained exception was: "+str(e), RNS.LOG_ERROR) RNS.log(f"The contained exception was: {e}", RNS.LOG_ERROR)
RNS.panic() RNS.panic()
else: else:
RNS.log("The interface name \""+name+"\" was already used. Check your configuration file for errors!", RNS.LOG_ERROR) RNS.log(f"The interface name \"{name}\" was already used. Check your configuration file for errors!", RNS.LOG_ERROR)
RNS.panic() RNS.panic()
RNS.log("System interfaces are ready", RNS.LOG_VERBOSE) RNS.log("System interfaces are ready", RNS.LOG_VERBOSE)
@ -1181,27 +1182,27 @@ class Reticulum:
for filename in os.listdir(self.resourcepath): for filename in os.listdir(self.resourcepath):
try: try:
if len(filename) == (RNS.Identity.HASHLENGTH//8)*2: if len(filename) == (RNS.Identity.HASHLENGTH//8)*2:
filepath = self.resourcepath + "/" + filename filepath = f"{self.resourcepath}/{filename}"
mtime = os.path.getmtime(filepath) mtime = os.path.getmtime(filepath)
age = now - mtime age = now - mtime
if age > Reticulum.RESOURCE_CACHE: if age > Reticulum.RESOURCE_CACHE:
os.unlink(filepath) os.unlink(filepath)
except Exception as e: except Exception as e:
RNS.log("Error while cleaning resources cache, the contained exception was: "+str(e), RNS.LOG_ERROR) RNS.log(f"Error while cleaning resources cache, the contained exception was: {e}", RNS.LOG_ERROR)
# Clean packet caches # Clean packet caches
for filename in os.listdir(self.cachepath): for filename in os.listdir(self.cachepath):
try: try:
if len(filename) == (RNS.Identity.HASHLENGTH//8)*2: if len(filename) == (RNS.Identity.HASHLENGTH//8)*2:
filepath = self.cachepath + "/" + filename filepath = f"{self.cachepath}/{filename}"
mtime = os.path.getmtime(filepath) mtime = os.path.getmtime(filepath)
age = now - mtime age = now - mtime
if age > RNS.Transport.DESTINATION_TIMEOUT: if age > RNS.Transport.DESTINATION_TIMEOUT:
os.unlink(filepath) os.unlink(filepath)
except Exception as e: except Exception as e:
RNS.log("Error while cleaning resources cache, the contained exception was: "+str(e), RNS.LOG_ERROR) RNS.log(f"Error while cleaning resources cache, the contained exception was: {e}", RNS.LOG_ERROR)
def __create_default_config(self): def __create_default_config(self):
self.config = ConfigObj(__default_rns_config__) self.config = ConfigObj(__default_rns_config__)
@ -1266,7 +1267,7 @@ class Reticulum:
rpc_connection.close() rpc_connection.close()
except Exception as e: except Exception as e:
RNS.log("An error ocurred while handling RPC call from local client: "+str(e), RNS.LOG_ERROR) RNS.log(f"An error ocurred while handling RPC call from local client: {e}", RNS.LOG_ERROR)
def get_interface_stats(self): def get_interface_stats(self):
if self.is_connected_to_shared_instance: if self.is_connected_to_shared_instance:
@ -1296,7 +1297,7 @@ class Reticulum:
if hasattr(interface, "b32"): if hasattr(interface, "b32"):
if interface.b32 != None: if interface.b32 != None:
ifstats["i2p_b32"] = interface.b32+".b32.i2p" ifstats["i2p_b32"] = f"{interface.b32}.b32.i2p"
else: else:
ifstats["i2p_b32"] = None ifstats["i2p_b32"] = None
@ -1327,7 +1328,7 @@ class Reticulum:
if hasattr(interface, "r_battery_state"): if hasattr(interface, "r_battery_state"):
if interface.r_battery_state != 0x00: if interface.r_battery_state != 0x00:
ifstats["battery_state"] = interface.r_battery_state ifstats["battery_state"] = interface.get_battery_state_string()
if hasattr(interface, "r_battery_percent"): if hasattr(interface, "r_battery_percent"):
ifstats["battery_percent"] = interface.r_battery_percent ifstats["battery_percent"] = interface.r_battery_percent
@ -1485,12 +1486,12 @@ class Reticulum:
if self.is_connected_to_shared_instance and hasattr(self, "_force_shared_instance_bitrate") and self._force_shared_instance_bitrate: if self.is_connected_to_shared_instance and hasattr(self, "_force_shared_instance_bitrate") and self._force_shared_instance_bitrate:
simulated_latency = ((1/self._force_shared_instance_bitrate)*8)*RNS.Reticulum.MTU simulated_latency = ((1/self._force_shared_instance_bitrate)*8)*RNS.Reticulum.MTU
RNS.log("Adding simulated latency of "+RNS.prettytime(simulated_latency)+" to first hop timeout", RNS.LOG_DEBUG) RNS.log(f"Adding simulated latency of {RNS.prettytime(simulated_latency)} to first hop timeout", RNS.LOG_DEBUG)
response += simulated_latency response += simulated_latency
return response return response
except Exception as e: except Exception as e:
RNS.log("An error occurred while getting first hop timeout from shared instance: "+str(e), RNS.LOG_ERROR) RNS.log(f"An error occurred while getting first hop timeout from shared instance: {e}", RNS.LOG_ERROR)
return RNS.Reticulum.DEFAULT_PER_HOP_TIMEOUT return RNS.Reticulum.DEFAULT_PER_HOP_TIMEOUT
else: else:

View File

@ -148,7 +148,7 @@ class Transport:
Transport.owner = reticulum_instance Transport.owner = reticulum_instance
if Transport.identity == None: if Transport.identity == None:
transport_identity_path = RNS.Reticulum.storagepath+"/transport_identity" transport_identity_path = f"{RNS.Reticulum.storagepath}/transport_identity"
if os.path.isfile(transport_identity_path): if os.path.isfile(transport_identity_path):
Transport.identity = RNS.Identity.from_file(transport_identity_path) Transport.identity = RNS.Identity.from_file(transport_identity_path)
@ -159,7 +159,7 @@ class Transport:
else: else:
RNS.log("Loaded Transport Identity from storage", RNS.LOG_VERBOSE) RNS.log("Loaded Transport Identity from storage", RNS.LOG_VERBOSE)
packet_hashlist_path = RNS.Reticulum.storagepath+"/packet_hashlist" packet_hashlist_path = f"{RNS.Reticulum.storagepath}/packet_hashlist"
if not Transport.owner.is_connected_to_shared_instance: if not Transport.owner.is_connected_to_shared_instance:
if os.path.isfile(packet_hashlist_path): if os.path.isfile(packet_hashlist_path):
try: try:
@ -167,7 +167,7 @@ class Transport:
Transport.packet_hashlist = umsgpack.unpackb(file.read()) Transport.packet_hashlist = umsgpack.unpackb(file.read())
file.close() file.close()
except Exception as e: except Exception as e:
RNS.log("Could not load packet hashlist from storage, the contained exception was: "+str(e), RNS.LOG_ERROR) RNS.log(f"Could not load packet hashlist from storage, the contained exception was: {e}", RNS.LOG_ERROR)
# Create transport-specific destinations # Create transport-specific destinations
Transport.path_request_destination = RNS.Destination(None, RNS.Destination.IN, RNS.Destination.PLAIN, Transport.APP_NAME, "path", "request") Transport.path_request_destination = RNS.Destination(None, RNS.Destination.IN, RNS.Destination.PLAIN, Transport.APP_NAME, "path", "request")
@ -186,15 +186,15 @@ class Transport:
Transport.remote_management_destination.register_request_handler("/path", response_generator = Transport.remote_path_handler, allow = RNS.Destination.ALLOW_LIST, allowed_list=Transport.remote_management_allowed) Transport.remote_management_destination.register_request_handler("/path", response_generator = Transport.remote_path_handler, allow = RNS.Destination.ALLOW_LIST, allowed_list=Transport.remote_management_allowed)
Transport.control_destinations.append(Transport.remote_management_destination) Transport.control_destinations.append(Transport.remote_management_destination)
Transport.control_hashes.append(Transport.remote_management_destination.hash) Transport.control_hashes.append(Transport.remote_management_destination.hash)
RNS.log("Enabled remote management on "+str(Transport.remote_management_destination), RNS.LOG_NOTICE) RNS.log(f"Enabled remote management on {Transport.remote_management_destination}", RNS.LOG_NOTICE)
Transport.jobs_running = False Transport.jobs_running = False
thread = threading.Thread(target=Transport.jobloop, daemon=True) thread = threading.Thread(target=Transport.jobloop, daemon=True)
thread.start() thread.start()
if RNS.Reticulum.transport_enabled(): if RNS.Reticulum.transport_enabled():
destination_table_path = RNS.Reticulum.storagepath+"/destination_table" destination_table_path = f"{RNS.Reticulum.storagepath}/destination_table"
tunnel_table_path = RNS.Reticulum.storagepath+"/tunnels" tunnel_table_path = f"{RNS.Reticulum.storagepath}/tunnels"
if os.path.isfile(destination_table_path) and not Transport.owner.is_connected_to_shared_instance: if os.path.isfile(destination_table_path) and not Transport.owner.is_connected_to_shared_instance:
serialised_destinations = [] serialised_destinations = []
@ -223,9 +223,9 @@ class Transport:
# increased hop-count. # increased hop-count.
announce_packet.hops += 1 announce_packet.hops += 1
Transport.destination_table[destination_hash] = [timestamp, received_from, hops, expires, random_blobs, receiving_interface, announce_packet] Transport.destination_table[destination_hash] = [timestamp, received_from, hops, expires, random_blobs, receiving_interface, announce_packet]
RNS.log("Loaded path table entry for "+RNS.prettyhexrep(destination_hash)+" from storage", RNS.LOG_DEBUG) RNS.log(f"Loaded path table entry for {RNS.prettyhexrep(destination_hash)} from storage", RNS.LOG_DEBUG)
else: else:
RNS.log("Could not reconstruct path table entry from storage for "+RNS.prettyhexrep(destination_hash), RNS.LOG_DEBUG) RNS.log(f"Could not reconstruct path table entry from storage for {RNS.prettyhexrep(destination_hash)}", RNS.LOG_DEBUG)
if announce_packet == None: if announce_packet == None:
RNS.log("The announce packet could not be loaded from cache", RNS.LOG_DEBUG) RNS.log("The announce packet could not be loaded from cache", RNS.LOG_DEBUG)
if receiving_interface == None: if receiving_interface == None:
@ -236,10 +236,10 @@ class Transport:
else: else:
specifier = "entries" specifier = "entries"
RNS.log("Loaded "+str(len(Transport.destination_table))+" path table "+specifier+" from storage", RNS.LOG_VERBOSE) RNS.log(f"Loaded {len(Transport.destination_table)} path table {specifier} from storage", RNS.LOG_VERBOSE)
except Exception as e: except Exception as e:
RNS.log("Could not load destination table from storage, the contained exception was: "+str(e), RNS.LOG_ERROR) RNS.log(f"Could not load destination table from storage, the contained exception was: {e}", RNS.LOG_ERROR)
if os.path.isfile(tunnel_table_path) and not Transport.owner.is_connected_to_shared_instance: if os.path.isfile(tunnel_table_path) and not Transport.owner.is_connected_to_shared_instance:
serialised_tunnels = [] serialised_tunnels = []
@ -284,21 +284,21 @@ class Transport:
else: else:
specifier = "entries" specifier = "entries"
RNS.log("Loaded "+str(len(Transport.tunnels))+" tunnel table "+specifier+" from storage", RNS.LOG_VERBOSE) RNS.log(f"Loaded {len(Transport.tunnels)} tunnel table {specifier} from storage", RNS.LOG_VERBOSE)
except Exception as e: except Exception as e:
RNS.log("Could not load tunnel table from storage, the contained exception was: "+str(e), RNS.LOG_ERROR) RNS.log(f"Could not load tunnel table from storage, the contained exception was: {e}", RNS.LOG_ERROR)
if RNS.Reticulum.probe_destination_enabled(): if RNS.Reticulum.probe_destination_enabled():
Transport.probe_destination = RNS.Destination(Transport.identity, RNS.Destination.IN, RNS.Destination.SINGLE, Transport.APP_NAME, "probe") Transport.probe_destination = RNS.Destination(Transport.identity, RNS.Destination.IN, RNS.Destination.SINGLE, Transport.APP_NAME, "probe")
Transport.probe_destination.accepts_links(False) Transport.probe_destination.accepts_links(False)
Transport.probe_destination.set_proof_strategy(RNS.Destination.PROVE_ALL) Transport.probe_destination.set_proof_strategy(RNS.Destination.PROVE_ALL)
Transport.probe_destination.announce() Transport.probe_destination.announce()
RNS.log("Transport Instance will respond to probe requests on "+str(Transport.probe_destination), RNS.LOG_NOTICE) RNS.log(f"Transport Instance will respond to probe requests on {Transport.probe_destination}", RNS.LOG_NOTICE)
else: else:
Transport.probe_destination = None Transport.probe_destination = None
RNS.log("Transport instance "+str(Transport.identity)+" started", RNS.LOG_VERBOSE) RNS.log(f"Transport instance {Transport.identity} started", RNS.LOG_VERBOSE)
Transport.start_time = time.time() Transport.start_time = time.time()
# Sort interfaces according to bitrate # Sort interfaces according to bitrate
@ -354,7 +354,7 @@ class Transport:
last_path_request = Transport.path_requests[link.destination.hash] last_path_request = Transport.path_requests[link.destination.hash]
if time.time() - last_path_request > Transport.PATH_REQUEST_MI: if time.time() - last_path_request > Transport.PATH_REQUEST_MI:
RNS.log("Trying to rediscover path for "+RNS.prettyhexrep(link.destination.hash)+" since an attempted link was never established", RNS.LOG_DEBUG) RNS.log(f"Trying to rediscover path for {RNS.prettyhexrep(link.destination.hash)} since an attempted link was never established", RNS.LOG_DEBUG)
if not link.destination.hash in path_requests: if not link.destination.hash in path_requests:
blocked_if = None blocked_if = None
path_requests[link.destination.hash] = blocked_if path_requests[link.destination.hash] = blocked_if
@ -388,7 +388,7 @@ class Transport:
for destination_hash in Transport.announce_table: for destination_hash in Transport.announce_table:
announce_entry = Transport.announce_table[destination_hash] announce_entry = Transport.announce_table[destination_hash]
if announce_entry[2] > Transport.PATHFINDER_R: if announce_entry[2] > Transport.PATHFINDER_R:
RNS.log("Completed announce processing for "+RNS.prettyhexrep(destination_hash)+", retry limit reached", RNS.LOG_EXTREME) RNS.log(f"Completed announce processing for {RNS.prettyhexrep(destination_hash)}, retry limit reached", RNS.LOG_EXTREME)
completed_announces.append(destination_hash) completed_announces.append(destination_hash)
else: else:
if time.time() > announce_entry[1]: if time.time() > announce_entry[1]:
@ -420,9 +420,9 @@ class Transport:
new_packet.hops = announce_entry[4] new_packet.hops = announce_entry[4]
if block_rebroadcasts: if block_rebroadcasts:
RNS.log("Rebroadcasting announce as path response for "+RNS.prettyhexrep(announce_destination.hash)+" with hop count "+str(new_packet.hops), RNS.LOG_DEBUG) RNS.log(f"Rebroadcasting announce as path response for {RNS.prettyhexrep(announce_destination.hash)} with hop count {new_packet.hops}", RNS.LOG_DEBUG)
else: else:
RNS.log("Rebroadcasting announce for "+RNS.prettyhexrep(announce_destination.hash)+" with hop count "+str(new_packet.hops), RNS.LOG_DEBUG) RNS.log(f"Rebroadcasting announce for {RNS.prettyhexrep(announce_destination.hash)} with hop count {new_packet.hops}", RNS.LOG_DEBUG)
outgoing.append(new_packet) outgoing.append(new_packet)
@ -490,14 +490,14 @@ class Transport:
# If the path has been invalidated between the time of # If the path has been invalidated between the time of
# making the link request and now, try to rediscover it # making the link request and now, try to rediscover it
if not Transport.has_path(link_entry[6]): if not Transport.has_path(link_entry[6]):
RNS.log("Trying to rediscover path for "+RNS.prettyhexrep(link_entry[6])+" since an attempted link was never established, and path is now missing", RNS.LOG_DEBUG) RNS.log(f"Trying to rediscover path for {RNS.prettyhexrep(link_entry[6])} since an attempted link was never established, and path is now missing", RNS.LOG_DEBUG)
path_request_conditions =True path_request_conditions =True
# If this link request was originated from a local client # If this link request was originated from a local client
# attempt to rediscover a path to the destination, if this # attempt to rediscover a path to the destination, if this
# has not already happened recently. # has not already happened recently.
elif not path_request_throttle and lr_taken_hops == 0: elif not path_request_throttle and lr_taken_hops == 0:
RNS.log("Trying to rediscover path for "+RNS.prettyhexrep(link_entry[6])+" since an attempted local client link was never established", RNS.LOG_DEBUG) RNS.log(f"Trying to rediscover path for {RNS.prettyhexrep(link_entry[6])} since an attempted local client link was never established", RNS.LOG_DEBUG)
path_request_conditions = True path_request_conditions = True
# If the link destination was previously only 1 hop # If the link destination was previously only 1 hop
@ -506,7 +506,7 @@ class Transport:
# In that case, try to discover a new path, and mark # In that case, try to discover a new path, and mark
# the old one as unresponsive. # the old one as unresponsive.
elif not path_request_throttle and Transport.hops_to(link_entry[6]) == 1: elif not path_request_throttle and Transport.hops_to(link_entry[6]) == 1:
RNS.log("Trying to rediscover path for "+RNS.prettyhexrep(link_entry[6])+" since an attempted link was never established, and destination was previously local to an interface on this instance", RNS.LOG_DEBUG) RNS.log(f"Trying to rediscover path for {RNS.prettyhexrep(link_entry[6])} since an attempted link was never established, and destination was previously local to an interface on this instance", RNS.LOG_DEBUG)
path_request_conditions = True path_request_conditions = True
blocked_if = link_entry[4] blocked_if = link_entry[4]
@ -528,7 +528,7 @@ class Transport:
# changed. In that case, we try to discover a new path, # changed. In that case, we try to discover a new path,
# and mark the old one as potentially unresponsive. # and mark the old one as potentially unresponsive.
elif not path_request_throttle and lr_taken_hops == 1: elif not path_request_throttle and lr_taken_hops == 1:
RNS.log("Trying to rediscover path for "+RNS.prettyhexrep(link_entry[6])+" since an attempted link was never established, and link initiator is local to an interface on this instance", RNS.LOG_DEBUG) RNS.log(f"Trying to rediscover path for {RNS.prettyhexrep(link_entry[6])} since an attempted link was never established, and link initiator is local to an interface on this instance", RNS.LOG_DEBUG)
path_request_conditions = True path_request_conditions = True
blocked_if = link_entry[4] blocked_if = link_entry[4]
@ -561,10 +561,10 @@ class Transport:
if time.time() > destination_expiry: if time.time() > destination_expiry:
stale_paths.append(destination_hash) stale_paths.append(destination_hash)
RNS.log("Path to "+RNS.prettyhexrep(destination_hash)+" timed out and was removed", RNS.LOG_DEBUG) RNS.log(f"Path to {RNS.prettyhexrep(destination_hash)} timed out and was removed", RNS.LOG_DEBUG)
elif not attached_interface in Transport.interfaces: elif not attached_interface in Transport.interfaces:
stale_paths.append(destination_hash) stale_paths.append(destination_hash)
RNS.log("Path to "+RNS.prettyhexrep(destination_hash)+" was removed since the attached interface no longer exists", RNS.LOG_DEBUG) RNS.log(f"Path to {RNS.prettyhexrep(destination_hash)} was removed since the attached interface no longer exists", RNS.LOG_DEBUG)
# Cull the pending discovery path requests table # Cull the pending discovery path requests table
stale_discovery_path_requests = [] stale_discovery_path_requests = []
@ -573,7 +573,7 @@ class Transport:
if time.time() > entry["timeout"]: if time.time() > entry["timeout"]:
stale_discovery_path_requests.append(destination_hash) stale_discovery_path_requests.append(destination_hash)
RNS.log("Waiting path request for "+RNS.prettyhexrep(destination_hash)+" timed out and was removed", RNS.LOG_DEBUG) RNS.log(f"Waiting path request for {RNS.prettyhexrep(destination_hash)} timed out and was removed", RNS.LOG_DEBUG)
# Cull the tunnel table # Cull the tunnel table
stale_tunnels = [] stale_tunnels = []
@ -584,7 +584,7 @@ class Transport:
expires = tunnel_entry[3] expires = tunnel_entry[3]
if time.time() > expires: if time.time() > expires:
stale_tunnels.append(tunnel_id) stale_tunnels.append(tunnel_id)
RNS.log("Tunnel "+RNS.prettyhexrep(tunnel_id)+" timed out and was removed", RNS.LOG_EXTREME) RNS.log(f"Tunnel {RNS.prettyhexrep(tunnel_id)} timed out and was removed", RNS.LOG_EXTREME)
else: else:
stale_tunnel_paths = [] stale_tunnel_paths = []
tunnel_paths = tunnel_entry[2] tunnel_paths = tunnel_entry[2]
@ -593,7 +593,7 @@ class Transport:
if time.time() > tunnel_path_entry[0] + Transport.DESTINATION_TIMEOUT: if time.time() > tunnel_path_entry[0] + Transport.DESTINATION_TIMEOUT:
stale_tunnel_paths.append(tunnel_path) stale_tunnel_paths.append(tunnel_path)
RNS.log("Tunnel path to "+RNS.prettyhexrep(tunnel_path)+" timed out and was removed", RNS.LOG_EXTREME) RNS.log(f"Tunnel path to {RNS.prettyhexrep(tunnel_path)} timed out and was removed", RNS.LOG_EXTREME)
for tunnel_path in stale_tunnel_paths: for tunnel_path in stale_tunnel_paths:
tunnel_paths.pop(tunnel_path) tunnel_paths.pop(tunnel_path)
@ -602,9 +602,9 @@ class Transport:
if ti > 0: if ti > 0:
if ti == 1: if ti == 1:
RNS.log("Removed "+str(ti)+" tunnel path", RNS.LOG_EXTREME) RNS.log(f"Removed {ti} tunnel path", RNS.LOG_EXTREME)
else: else:
RNS.log("Removed "+str(ti)+" tunnel paths", RNS.LOG_EXTREME) RNS.log(f"Removed {ti} tunnel paths", RNS.LOG_EXTREME)
i = 0 i = 0
for truncated_packet_hash in stale_reverse_entries: for truncated_packet_hash in stale_reverse_entries:
@ -613,9 +613,9 @@ class Transport:
if i > 0: if i > 0:
if i == 1: if i == 1:
RNS.log("Released "+str(i)+" reverse table entry", RNS.LOG_EXTREME) RNS.log(f"Released {i} reverse table entry", RNS.LOG_EXTREME)
else: else:
RNS.log("Released "+str(i)+" reverse table entries", RNS.LOG_EXTREME) RNS.log(f"Released {i} reverse table entries", RNS.LOG_EXTREME)
i = 0 i = 0
for link_id in stale_links: for link_id in stale_links:
@ -624,9 +624,9 @@ class Transport:
if i > 0: if i > 0:
if i == 1: if i == 1:
RNS.log("Released "+str(i)+" link", RNS.LOG_EXTREME) RNS.log(f"Released {i} link", RNS.LOG_EXTREME)
else: else:
RNS.log("Released "+str(i)+" links", RNS.LOG_EXTREME) RNS.log(f"Released {i} links", RNS.LOG_EXTREME)
i = 0 i = 0
for destination_hash in stale_paths: for destination_hash in stale_paths:
@ -635,9 +635,9 @@ class Transport:
if i > 0: if i > 0:
if i == 1: if i == 1:
RNS.log("Removed "+str(i)+" path", RNS.LOG_EXTREME) RNS.log(f"Removed {i} path", RNS.LOG_EXTREME)
else: else:
RNS.log("Removed "+str(i)+" paths", RNS.LOG_EXTREME) RNS.log(f"Removed {i} paths", RNS.LOG_EXTREME)
i = 0 i = 0
for destination_hash in stale_discovery_path_requests: for destination_hash in stale_discovery_path_requests:
@ -646,9 +646,9 @@ class Transport:
if i > 0: if i > 0:
if i == 1: if i == 1:
RNS.log("Removed "+str(i)+" waiting path request", RNS.LOG_EXTREME) RNS.log(f"Removed {i} waiting path request", RNS.LOG_EXTREME)
else: else:
RNS.log("Removed "+str(i)+" waiting path requests", RNS.LOG_EXTREME) RNS.log(f"Removed {i} waiting path requests", RNS.LOG_EXTREME)
i = 0 i = 0
for tunnel_id in stale_tunnels: for tunnel_id in stale_tunnels:
@ -657,9 +657,9 @@ class Transport:
if i > 0: if i > 0:
if i == 1: if i == 1:
RNS.log("Removed "+str(i)+" tunnel", RNS.LOG_EXTREME) RNS.log(f"Removed {i} tunnel", RNS.LOG_EXTREME)
else: else:
RNS.log("Removed "+str(i)+" tunnels", RNS.LOG_EXTREME) RNS.log(f"Removed {i} tunnels", RNS.LOG_EXTREME)
i = 0 i = 0
for destination_hash in stale_path_states: for destination_hash in stale_path_states:
@ -668,9 +668,9 @@ class Transport:
if i > 0: if i > 0:
if i == 1: if i == 1:
RNS.log("Removed "+str(i)+" path state entry", RNS.LOG_EXTREME) RNS.log(f"Removed {i} path state entry", RNS.LOG_EXTREME)
else: else:
RNS.log("Removed "+str(i)+" path state entries", RNS.LOG_EXTREME) RNS.log(f"Removed {i} path state entries", RNS.LOG_EXTREME)
Transport.tables_last_culled = time.time() Transport.tables_last_culled = time.time()
@ -686,7 +686,7 @@ class Transport:
except Exception as e: except Exception as e:
RNS.log("An exception occurred while running Transport jobs.", RNS.LOG_ERROR) RNS.log("An exception occurred while running Transport jobs.", RNS.LOG_ERROR)
RNS.log("The contained exception was: "+str(e), RNS.LOG_ERROR) RNS.log(f"The contained exception was: {e}", RNS.LOG_ERROR)
Transport.jobs_running = False Transport.jobs_running = False
@ -749,7 +749,7 @@ class Transport:
interface.processOutgoing(raw) interface.processOutgoing(raw)
except Exception as e: except Exception as e:
RNS.log("Error while transmitting on "+str(interface)+". The contained exception was: "+str(e), RNS.LOG_ERROR) RNS.log(f"Error while transmitting on {interface}. The contained exception was: {e}", RNS.LOG_ERROR)
@staticmethod @staticmethod
def outbound(packet): def outbound(packet):
@ -857,7 +857,7 @@ class Transport:
if packet.packet_type == RNS.Packet.ANNOUNCE: if packet.packet_type == RNS.Packet.ANNOUNCE:
if packet.attached_interface == None: if packet.attached_interface == None:
if interface.mode == RNS.Interfaces.Interface.Interface.MODE_ACCESS_POINT: if interface.mode == RNS.Interfaces.Interface.Interface.MODE_ACCESS_POINT:
RNS.log("Blocking announce broadcast on "+str(interface)+" due to AP mode", RNS.LOG_EXTREME) RNS.log(f"Blocking announce broadcast on {interface} due to AP mode", RNS.LOG_EXTREME)
should_transmit = False should_transmit = False
elif interface.mode == RNS.Interfaces.Interface.Interface.MODE_ROAMING: elif interface.mode == RNS.Interfaces.Interface.Interface.MODE_ROAMING:
@ -870,15 +870,15 @@ class Transport:
if from_interface == None or not hasattr(from_interface, "mode"): if from_interface == None or not hasattr(from_interface, "mode"):
should_transmit = False should_transmit = False
if from_interface == None: if from_interface == None:
RNS.log("Blocking announce broadcast on "+str(interface)+" since next hop interface doesn't exist", RNS.LOG_EXTREME) RNS.log(f"Blocking announce broadcast on {interface} since next hop interface doesn't exist", RNS.LOG_EXTREME)
elif not hasattr(from_interface, "mode"): elif not hasattr(from_interface, "mode"):
RNS.log("Blocking announce broadcast on "+str(interface)+" since next hop interface has no mode configured", RNS.LOG_EXTREME) RNS.log(f"Blocking announce broadcast on {interface} since next hop interface has no mode configured", RNS.LOG_EXTREME)
else: else:
if from_interface.mode == RNS.Interfaces.Interface.Interface.MODE_ROAMING: if from_interface.mode == RNS.Interfaces.Interface.Interface.MODE_ROAMING:
RNS.log("Blocking announce broadcast on "+str(interface)+" due to roaming-mode next-hop interface", RNS.LOG_EXTREME) RNS.log(f"Blocking announce broadcast on {interface} due to roaming-mode next-hop interface", RNS.LOG_EXTREME)
should_transmit = False should_transmit = False
elif from_interface.mode == RNS.Interfaces.Interface.Interface.MODE_BOUNDARY: elif from_interface.mode == RNS.Interfaces.Interface.Interface.MODE_BOUNDARY:
RNS.log("Blocking announce broadcast on "+str(interface)+" due to boundary-mode next-hop interface", RNS.LOG_EXTREME) RNS.log(f"Blocking announce broadcast on {interface} due to boundary-mode next-hop interface", RNS.LOG_EXTREME)
should_transmit = False should_transmit = False
elif interface.mode == RNS.Interfaces.Interface.Interface.MODE_BOUNDARY: elif interface.mode == RNS.Interfaces.Interface.Interface.MODE_BOUNDARY:
@ -891,12 +891,12 @@ class Transport:
if from_interface == None or not hasattr(from_interface, "mode"): if from_interface == None or not hasattr(from_interface, "mode"):
should_transmit = False should_transmit = False
if from_interface == None: if from_interface == None:
RNS.log("Blocking announce broadcast on "+str(interface)+" since next hop interface doesn't exist", RNS.LOG_EXTREME) RNS.log(f"Blocking announce broadcast on {interface} since next hop interface doesn't exist", RNS.LOG_EXTREME)
elif not hasattr(from_interface, "mode"): elif not hasattr(from_interface, "mode"):
RNS.log("Blocking announce broadcast on "+str(interface)+" since next hop interface has no mode configured", RNS.LOG_EXTREME) RNS.log(f"Blocking announce broadcast on {interface} since next hop interface has no mode configured", RNS.LOG_EXTREME)
else: else:
if from_interface.mode == RNS.Interfaces.Interface.Interface.MODE_ROAMING: if from_interface.mode == RNS.Interfaces.Interface.Interface.MODE_ROAMING:
RNS.log("Blocking announce broadcast on "+str(interface)+" due to roaming-mode next-hop interface", RNS.LOG_EXTREME) RNS.log(f"Blocking announce broadcast on {interface} due to roaming-mode next-hop interface", RNS.LOG_EXTREME)
should_transmit = False should_transmit = False
else: else:
@ -959,23 +959,23 @@ class Transport:
timer.start() timer.start()
if wait_time < 1: if wait_time < 1:
wait_time_str = str(round(wait_time*1000,2))+"ms" wait_time_str = f"{round(wait_time * 1000, 2)}ms"
else: else:
wait_time_str = str(round(wait_time*1,2))+"s" wait_time_str = f"{round(wait_time * 1, 2)}s"
ql_str = str(len(interface.announce_queue)) ql_str = str(len(interface.announce_queue))
RNS.log("Added announce to queue (height "+ql_str+") on "+str(interface)+" for processing in "+wait_time_str, RNS.LOG_EXTREME) RNS.log(f"Added announce to queue (height {ql_str}) on {interface} for processing in {wait_time_str}", RNS.LOG_EXTREME)
else: else:
wait_time = max(interface.announce_allowed_at - time.time(), 0) wait_time = max(interface.announce_allowed_at - time.time(), 0)
if wait_time < 1: if wait_time < 1:
wait_time_str = str(round(wait_time*1000,2))+"ms" wait_time_str = f"{round(wait_time * 1000, 2)}ms"
else: else:
wait_time_str = str(round(wait_time*1,2))+"s" wait_time_str = f"{round(wait_time * 1, 2)}s"
ql_str = str(len(interface.announce_queue)) ql_str = str(len(interface.announce_queue))
RNS.log("Added announce to queue (height "+ql_str+") on "+str(interface)+" for processing in "+wait_time_str, RNS.LOG_EXTREME) RNS.log(f"Added announce to queue (height {ql_str}) on {interface} for processing in {wait_time_str}", RNS.LOG_EXTREME)
else: else:
pass pass
@ -1013,7 +1013,7 @@ class Transport:
# Filter packets intended for other transport instances # Filter packets intended for other transport instances
if packet.transport_id != None and packet.packet_type != RNS.Packet.ANNOUNCE: if packet.transport_id != None and packet.packet_type != RNS.Packet.ANNOUNCE:
if packet.transport_id != Transport.identity.hash: if packet.transport_id != Transport.identity.hash:
RNS.log("Ignored packet "+RNS.prettyhexrep(packet.packet_hash)+" in transport for other transport instance", RNS.LOG_EXTREME) RNS.log(f"Ignored packet {RNS.prettyhexrep(packet.packet_hash)} in transport for other transport instance", RNS.LOG_EXTREME)
return False return False
if packet.context == RNS.Packet.KEEPALIVE: if packet.context == RNS.Packet.KEEPALIVE:
@ -1032,7 +1032,7 @@ class Transport:
if packet.destination_type == RNS.Destination.PLAIN: if packet.destination_type == RNS.Destination.PLAIN:
if packet.packet_type != RNS.Packet.ANNOUNCE: if packet.packet_type != RNS.Packet.ANNOUNCE:
if packet.hops > 1: if packet.hops > 1:
RNS.log("Dropped PLAIN packet "+RNS.prettyhexrep(packet.hash)+" with "+str(packet.hops)+" hops", RNS.LOG_DEBUG) RNS.log(f"Dropped PLAIN packet {RNS.prettyhexrep(packet.hash)} with {packet.hops} hops", RNS.LOG_DEBUG)
return False return False
else: else:
return True return True
@ -1043,7 +1043,7 @@ class Transport:
if packet.destination_type == RNS.Destination.GROUP: if packet.destination_type == RNS.Destination.GROUP:
if packet.packet_type != RNS.Packet.ANNOUNCE: if packet.packet_type != RNS.Packet.ANNOUNCE:
if packet.hops > 1: if packet.hops > 1:
RNS.log("Dropped GROUP packet "+RNS.prettyhexrep(packet.hash)+" with "+str(packet.hops)+" hops", RNS.LOG_DEBUG) RNS.log(f"Dropped GROUP packet {RNS.prettyhexrep(packet.hash)} with {packet.hops} hops", RNS.LOG_DEBUG)
return False return False
else: else:
return True return True
@ -1061,7 +1061,7 @@ class Transport:
RNS.log("Dropped invalid announce packet", RNS.LOG_DEBUG) RNS.log("Dropped invalid announce packet", RNS.LOG_DEBUG)
return False return False
RNS.log("Filtered packet with hash "+RNS.prettyhexrep(packet.packet_hash), RNS.LOG_EXTREME) RNS.log(f"Filtered packet with hash {RNS.prettyhexrep(packet.packet_hash)}", RNS.LOG_EXTREME)
return False return False
@staticmethod @staticmethod
@ -1316,7 +1316,7 @@ class Transport:
# TODO: There should probably be some kind of REJECT # TODO: There should probably be some kind of REJECT
# mechanism here, to signal to the source that their # mechanism here, to signal to the source that their
# expected path failed. # expected path failed.
RNS.log("Got packet in transport, but no known path to final destination "+RNS.prettyhexrep(packet.destination_hash)+". Dropping packet.", RNS.LOG_EXTREME) RNS.log(f"Got packet in transport, but no known path to final destination {RNS.prettyhexrep(packet.destination_hash)}. Dropping packet.", RNS.LOG_EXTREME)
# Link transport handling. Directs packets according # Link transport handling. Directs packets according
# to entries in the link tables # to entries in the link tables
@ -1391,17 +1391,17 @@ class Transport:
announce_entry = Transport.announce_table[packet.destination_hash] announce_entry = Transport.announce_table[packet.destination_hash]
if packet.hops-1 == announce_entry[4]: if packet.hops-1 == announce_entry[4]:
RNS.log("Heard a local rebroadcast of announce for "+RNS.prettyhexrep(packet.destination_hash), RNS.LOG_DEBUG) RNS.log(f"Heard a local rebroadcast of announce for {RNS.prettyhexrep(packet.destination_hash)}", RNS.LOG_DEBUG)
announce_entry[6] += 1 announce_entry[6] += 1
if announce_entry[6] >= Transport.LOCAL_REBROADCASTS_MAX: if announce_entry[6] >= Transport.LOCAL_REBROADCASTS_MAX:
RNS.log("Max local rebroadcasts of announce for "+RNS.prettyhexrep(packet.destination_hash)+" reached, dropping announce from our table", RNS.LOG_DEBUG) RNS.log(f"Max local rebroadcasts of announce for {RNS.prettyhexrep(packet.destination_hash)} reached, dropping announce from our table", RNS.LOG_DEBUG)
if packet.destination_hash in Transport.announce_table: if packet.destination_hash in Transport.announce_table:
Transport.announce_table.pop(packet.destination_hash) Transport.announce_table.pop(packet.destination_hash)
if packet.hops-1 == announce_entry[4]+1 and announce_entry[2] > 0: if packet.hops-1 == announce_entry[4]+1 and announce_entry[2] > 0:
now = time.time() now = time.time()
if now < announce_entry[1]: if now < announce_entry[1]:
RNS.log("Rebroadcasted announce for "+RNS.prettyhexrep(packet.destination_hash)+" has been passed on to another node, no further tries needed", RNS.LOG_DEBUG) RNS.log(f"Rebroadcasted announce for {RNS.prettyhexrep(packet.destination_hash)} has been passed on to another node, no further tries needed", RNS.LOG_DEBUG)
Transport.announce_table.pop(packet.destination_hash) Transport.announce_table.pop(packet.destination_hash)
else: else:
@ -1458,7 +1458,7 @@ class Transport:
if not random_blob in random_blobs: if not random_blob in random_blobs:
# TODO: Check that this ^ approach actually # TODO: Check that this ^ approach actually
# works under all circumstances # works under all circumstances
RNS.log("Replacing destination table entry for "+str(RNS.prettyhexrep(packet.destination_hash))+" with new announce due to expired path", RNS.LOG_DEBUG) RNS.log(f"Replacing destination table entry for {RNS.prettyhexrep(packet.destination_hash)} with new announce due to expired path", RNS.LOG_DEBUG)
Transport.mark_path_unknown_state(packet.destination_hash) Transport.mark_path_unknown_state(packet.destination_hash)
should_add = True should_add = True
else: else:
@ -1469,7 +1469,7 @@ class Transport:
# this announce before, update the path table. # this announce before, update the path table.
if (announce_emitted > path_announce_emitted): if (announce_emitted > path_announce_emitted):
if not random_blob in random_blobs: if not random_blob in random_blobs:
RNS.log("Replacing destination table entry for "+str(RNS.prettyhexrep(packet.destination_hash))+" with new announce, since it was more recently emitted", RNS.LOG_DEBUG) RNS.log(f"Replacing destination table entry for {RNS.prettyhexrep(packet.destination_hash)} with new announce, since it was more recently emitted", RNS.LOG_DEBUG)
Transport.mark_path_unknown_state(packet.destination_hash) Transport.mark_path_unknown_state(packet.destination_hash)
should_add = True should_add = True
else: else:
@ -1481,7 +1481,7 @@ class Transport:
# allow updating the path table to this one. # allow updating the path table to this one.
elif announce_emitted == path_announce_emitted: elif announce_emitted == path_announce_emitted:
if Transport.path_is_unresponsive(packet.destination_hash): if Transport.path_is_unresponsive(packet.destination_hash):
RNS.log("Replacing destination table entry for "+str(RNS.prettyhexrep(packet.destination_hash))+" with new announce, since previously tried path was unresponsive", RNS.LOG_DEBUG) RNS.log(f"Replacing destination table entry for {RNS.prettyhexrep(packet.destination_hash)} with new announce, since previously tried path was unresponsive", RNS.LOG_DEBUG)
should_add = True should_add = True
else: else:
should_add = False should_add = False
@ -1551,7 +1551,7 @@ class Transport:
# Insert announce into announce table for retransmission # Insert announce into announce table for retransmission
if rate_blocked: if rate_blocked:
RNS.log("Blocking rebroadcast of announce from "+RNS.prettyhexrep(packet.destination_hash)+" due to excessive announce rate", RNS.LOG_DEBUG) RNS.log(f"Blocking rebroadcast of announce from {RNS.prettyhexrep(packet.destination_hash)} due to excessive announce rate", RNS.LOG_DEBUG)
else: else:
if Transport.from_local_client(packet): if Transport.from_local_client(packet):
@ -1648,9 +1648,9 @@ class Transport:
pr_entry = Transport.discovery_path_requests[packet.destination_hash] pr_entry = Transport.discovery_path_requests[packet.destination_hash]
attached_interface = pr_entry["requesting_interface"] attached_interface = pr_entry["requesting_interface"]
interface_str = " on "+str(attached_interface) interface_str = f" on {attached_interface}"
RNS.log("Got matching announce, answering waiting discovery path request for "+RNS.prettyhexrep(packet.destination_hash)+interface_str, RNS.LOG_DEBUG) RNS.log(f"Got matching announce, answering waiting discovery path request for {RNS.prettyhexrep(packet.destination_hash)}{interface_str}", RNS.LOG_DEBUG)
announce_identity = RNS.Identity.recall(packet.destination_hash) announce_identity = RNS.Identity.recall(packet.destination_hash)
announce_destination = RNS.Destination(announce_identity, RNS.Destination.OUT, RNS.Destination.SINGLE, "unknown", "unknown"); announce_destination = RNS.Destination(announce_identity, RNS.Destination.OUT, RNS.Destination.SINGLE, "unknown", "unknown");
announce_destination.hash = packet.destination_hash announce_destination.hash = packet.destination_hash
@ -1675,7 +1675,7 @@ class Transport:
destination_table_entry = [now, received_from, announce_hops, expires, random_blobs, packet.receiving_interface, packet] destination_table_entry = [now, received_from, announce_hops, expires, random_blobs, packet.receiving_interface, packet]
Transport.destination_table[packet.destination_hash] = destination_table_entry Transport.destination_table[packet.destination_hash] = destination_table_entry
RNS.log("Destination "+RNS.prettyhexrep(packet.destination_hash)+" is now "+str(announce_hops)+" hops away via "+RNS.prettyhexrep(received_from)+" on "+str(packet.receiving_interface), RNS.LOG_DEBUG) RNS.log(f"Destination {RNS.prettyhexrep(packet.destination_hash)} is now {announce_hops} hops away via {RNS.prettyhexrep(received_from)} on {packet.receiving_interface}", RNS.LOG_DEBUG)
# If the receiving interface is a tunnel, we add the # If the receiving interface is a tunnel, we add the
# announce to the tunnels table # announce to the tunnels table
@ -1685,7 +1685,7 @@ class Transport:
paths[packet.destination_hash] = destination_table_entry paths[packet.destination_hash] = destination_table_entry
expires = time.time() + Transport.DESTINATION_TIMEOUT expires = time.time() + Transport.DESTINATION_TIMEOUT
tunnel_entry[3] = expires tunnel_entry[3] = expires
RNS.log("Path to "+RNS.prettyhexrep(packet.destination_hash)+" associated with tunnel "+RNS.prettyhexrep(packet.receiving_interface.tunnel_id), RNS.LOG_DEBUG) RNS.log(f"Path to {RNS.prettyhexrep(packet.destination_hash)} associated with tunnel {RNS.prettyhexrep(packet.receiving_interface.tunnel_id)}", RNS.LOG_DEBUG)
# Call externally registered callbacks from apps # Call externally registered callbacks from apps
# wanting to know when an announce arrives # wanting to know when an announce arrives
@ -1720,7 +1720,7 @@ class Transport:
) )
except Exception as e: except Exception as e:
RNS.log("Error while processing external announce callback.", RNS.LOG_ERROR) RNS.log("Error while processing external announce callback.", RNS.LOG_ERROR)
RNS.log("The contained exception was: "+str(e), RNS.LOG_ERROR) RNS.log(f"The contained exception was: {e}", RNS.LOG_ERROR)
RNS.trace_exception(e) RNS.trace_exception(e)
# Handling for link requests to local destinations # Handling for link requests to local destinations
@ -1738,7 +1738,16 @@ class Transport:
if link.link_id == packet.destination_hash: if link.link_id == packet.destination_hash:
if link.attached_interface == packet.receiving_interface: if link.attached_interface == packet.receiving_interface:
packet.link = link packet.link = link
link.receive(packet) if packet.context == RNS.Packet.CACHE_REQUEST:
cached_packet = Transport.get_cached_packet(packet.data)
if cached_packet != None:
cached_packet.unpack()
RNS.Packet(destination=link, data=cached_packet.data,
packet_type=cached_packet.packet_type, context=cached_packet.context).send()
Transport.jobs_locked = False
else:
link.receive(packet)
else: else:
# In the strange and rare case that an interface # In the strange and rare case that an interface
# is partly malfunctioning, and a link-associated # is partly malfunctioning, and a link-associated
@ -1764,7 +1773,7 @@ class Transport:
if destination.callbacks.proof_requested(packet): if destination.callbacks.proof_requested(packet):
packet.prove() packet.prove()
except Exception as e: except Exception as e:
RNS.log("Error while executing proof request callback. The contained exception was: "+str(e), RNS.LOG_ERROR) RNS.log(f"Error while executing proof request callback. The contained exception was: {e}", RNS.LOG_ERROR)
# Handling for proofs and link-request proofs # Handling for proofs and link-request proofs
elif packet.packet_type == RNS.Packet.PROOF: elif packet.packet_type == RNS.Packet.PROOF:
@ -1785,7 +1794,7 @@ class Transport:
signature = packet.data[:RNS.Identity.SIGLENGTH//8] signature = packet.data[:RNS.Identity.SIGLENGTH//8]
if peer_identity.validate(signature, signed_data): if peer_identity.validate(signature, signed_data):
RNS.log("Link request proof validated for transport via "+str(link_entry[4]), RNS.LOG_EXTREME) RNS.log(f"Link request proof validated for transport via {link_entry[4]}", RNS.LOG_EXTREME)
new_raw = packet.raw[0:1] new_raw = packet.raw[0:1]
new_raw += struct.pack("!B", packet.hops) new_raw += struct.pack("!B", packet.hops)
new_raw += packet.raw[2:] new_raw += packet.raw[2:]
@ -1793,10 +1802,10 @@ class Transport:
Transport.transmit(link_entry[4], new_raw) Transport.transmit(link_entry[4], new_raw)
else: else:
RNS.log("Invalid link request proof in transport for link "+RNS.prettyhexrep(packet.destination_hash)+", dropping proof.", RNS.LOG_DEBUG) RNS.log(f"Invalid link request proof in transport for link {RNS.prettyhexrep(packet.destination_hash)}, dropping proof.", RNS.LOG_DEBUG)
except Exception as e: except Exception as e:
RNS.log("Error while transporting link request proof. The contained exception was: "+str(e), RNS.LOG_ERROR) RNS.log(f"Error while transporting link request proof. The contained exception was: {e}", RNS.LOG_ERROR)
else: else:
RNS.log("Link request proof received on wrong interface, not transporting it.", RNS.LOG_DEBUG) RNS.log("Link request proof received on wrong interface, not transporting it.", RNS.LOG_DEBUG)
@ -1845,7 +1854,7 @@ class Transport:
if (RNS.Reticulum.transport_enabled() or from_local_client or proof_for_local_client) and packet.destination_hash in Transport.reverse_table: if (RNS.Reticulum.transport_enabled() or from_local_client or proof_for_local_client) and packet.destination_hash in Transport.reverse_table:
reverse_entry = Transport.reverse_table.pop(packet.destination_hash) reverse_entry = Transport.reverse_table.pop(packet.destination_hash)
if packet.receiving_interface == reverse_entry[1]: if packet.receiving_interface == reverse_entry[1]:
RNS.log("Proof received on correct interface, transporting it via "+str(reverse_entry[0]), RNS.LOG_EXTREME) RNS.log(f"Proof received on correct interface, transporting it via {reverse_entry[0]}", RNS.LOG_EXTREME)
new_raw = packet.raw[0:1] new_raw = packet.raw[0:1]
new_raw += struct.pack("!B", packet.hops) new_raw += struct.pack("!B", packet.hops)
new_raw += packet.raw[2:] new_raw += packet.raw[2:]
@ -1913,19 +1922,19 @@ class Transport:
except Exception as e: except Exception as e:
RNS.log("An error occurred while validating tunnel establishment packet.", RNS.LOG_DEBUG) RNS.log("An error occurred while validating tunnel establishment packet.", RNS.LOG_DEBUG)
RNS.log("The contained exception was: "+str(e), RNS.LOG_DEBUG) RNS.log(f"The contained exception was: {e}", RNS.LOG_DEBUG)
@staticmethod @staticmethod
def handle_tunnel(tunnel_id, interface): def handle_tunnel(tunnel_id, interface):
expires = time.time() + Transport.DESTINATION_TIMEOUT expires = time.time() + Transport.DESTINATION_TIMEOUT
if not tunnel_id in Transport.tunnels: if not tunnel_id in Transport.tunnels:
RNS.log("Tunnel endpoint "+RNS.prettyhexrep(tunnel_id)+" established.", RNS.LOG_DEBUG) RNS.log(f"Tunnel endpoint {RNS.prettyhexrep(tunnel_id)} established.", RNS.LOG_DEBUG)
paths = {} paths = {}
tunnel_entry = [tunnel_id, interface, paths, expires] tunnel_entry = [tunnel_id, interface, paths, expires]
interface.tunnel_id = tunnel_id interface.tunnel_id = tunnel_id
Transport.tunnels[tunnel_id] = tunnel_entry Transport.tunnels[tunnel_id] = tunnel_entry
else: else:
RNS.log("Tunnel endpoint "+RNS.prettyhexrep(tunnel_id)+" reappeared. Restoring paths...", RNS.LOG_DEBUG) RNS.log(f"Tunnel endpoint {RNS.prettyhexrep(tunnel_id)} reappeared. Restoring paths...", RNS.LOG_DEBUG)
tunnel_entry = Transport.tunnels[tunnel_id] tunnel_entry = Transport.tunnels[tunnel_id]
tunnel_entry[1] = interface tunnel_entry[1] = interface
tunnel_entry[3] = expires tunnel_entry[3] = expires
@ -1950,21 +1959,21 @@ class Transport:
if announce_hops <= old_hops or time.time() > old_expires: if announce_hops <= old_hops or time.time() > old_expires:
should_add = True should_add = True
else: else:
RNS.log("Did not restore path to "+RNS.prettyhexrep(packet.destination_hash)+" because a newer path with fewer hops exist", RNS.LOG_DEBUG) RNS.log(f"Did not restore path to {RNS.prettyhexrep(packet.destination_hash)} because a newer path with fewer hops exist", RNS.LOG_DEBUG)
else: else:
if time.time() < expires: if time.time() < expires:
should_add = True should_add = True
else: else:
RNS.log("Did not restore path to "+RNS.prettyhexrep(packet.destination_hash)+" because it has expired", RNS.LOG_DEBUG) RNS.log(f"Did not restore path to {RNS.prettyhexrep(packet.destination_hash)} because it has expired", RNS.LOG_DEBUG)
if should_add: if should_add:
Transport.destination_table[destination_hash] = new_entry Transport.destination_table[destination_hash] = new_entry
RNS.log("Restored path to "+RNS.prettyhexrep(packet.destination_hash)+" is now "+str(announce_hops)+" hops away via "+RNS.prettyhexrep(received_from)+" on "+str(receiving_interface), RNS.LOG_DEBUG) RNS.log(f"Restored path to {RNS.prettyhexrep(packet.destination_hash)} is now {announce_hops} hops away via {RNS.prettyhexrep(received_from)} on {receiving_interface}", RNS.LOG_DEBUG)
else: else:
deprecated_paths.append(destination_hash) deprecated_paths.append(destination_hash)
for deprecated_path in deprecated_paths: for deprecated_path in deprecated_paths:
RNS.log("Removing path to "+RNS.prettyhexrep(deprecated_path)+" from tunnel "+RNS.prettyhexrep(tunnel_id), RNS.LOG_DEBUG) RNS.log(f"Removing path to {RNS.prettyhexrep(deprecated_path)} from tunnel {RNS.prettyhexrep(tunnel_id)}", RNS.LOG_DEBUG)
paths.pop(deprecated_path) paths.pop(deprecated_path)
@staticmethod @staticmethod
@ -1988,7 +1997,7 @@ class Transport:
@staticmethod @staticmethod
def register_link(link): def register_link(link):
RNS.log("Registering link "+str(link), RNS.LOG_EXTREME) RNS.log(f"Registering link {link}", RNS.LOG_EXTREME)
if link.initiator: if link.initiator:
Transport.pending_links.append(link) Transport.pending_links.append(link)
else: else:
@ -1996,10 +2005,10 @@ class Transport:
@staticmethod @staticmethod
def activate_link(link): def activate_link(link):
RNS.log("Activating link "+str(link), RNS.LOG_EXTREME) RNS.log(f"Activating link {link}", RNS.LOG_EXTREME)
if link in Transport.pending_links: if link in Transport.pending_links:
if link.status != RNS.Link.ACTIVE: if link.status != RNS.Link.ACTIVE:
raise IOError("Invalid link state for link activation: "+str(link.status)) raise OSError(f"Invalid link state for link activation: {link.status}")
Transport.pending_links.remove(link) Transport.pending_links.remove(link)
Transport.active_links.append(link) Transport.active_links.append(link)
link.status = RNS.Link.ACTIVE link.status = RNS.Link.ACTIVE
@ -2061,18 +2070,18 @@ class Transport:
if packet.receiving_interface != None: if packet.receiving_interface != None:
interface_reference = str(packet.receiving_interface) interface_reference = str(packet.receiving_interface)
file = open(RNS.Reticulum.cachepath+"/"+packet_hash, "wb") file = open(f"{RNS.Reticulum.cachepath}/{packet_hash}", "wb")
file.write(umsgpack.packb([packet.raw, interface_reference])) file.write(umsgpack.packb([packet.raw, interface_reference]))
file.close() file.close()
except Exception as e: except Exception as e:
RNS.log("Error writing packet to cache. The contained exception was: "+str(e), RNS.LOG_ERROR) RNS.log(f"Error writing packet to cache. The contained exception was: {e}", RNS.LOG_ERROR)
@staticmethod @staticmethod
def get_cached_packet(packet_hash): def get_cached_packet(packet_hash):
try: try:
packet_hash = RNS.hexrep(packet_hash, delimit=False) packet_hash = RNS.hexrep(packet_hash, delimit=False)
path = RNS.Reticulum.cachepath+"/"+packet_hash path = f"{RNS.Reticulum.cachepath}/{packet_hash}"
if os.path.isfile(path): if os.path.isfile(path):
file = open(path, "rb") file = open(path, "rb")
@ -2091,7 +2100,7 @@ class Transport:
return None return None
except Exception as e: except Exception as e:
RNS.log("Exception occurred while getting cached packet.", RNS.LOG_ERROR) RNS.log("Exception occurred while getting cached packet.", RNS.LOG_ERROR)
RNS.log("The contained exception was: "+str(e), RNS.LOG_ERROR) RNS.log(f"The contained exception was: {e}", RNS.LOG_ERROR)
@staticmethod @staticmethod
def cache_request_packet(packet): def cache_request_packet(packet):
@ -2281,12 +2290,12 @@ class Transport:
queued_announces = True if len(on_interface.announce_queue) > 0 else False queued_announces = True if len(on_interface.announce_queue) > 0 else False
if queued_announces: if queued_announces:
RNS.log("Blocking recursive path request on "+str(on_interface)+" due to queued announces", RNS.LOG_EXTREME) RNS.log(f"Blocking recursive path request on {on_interface} due to queued announces", RNS.LOG_EXTREME)
return return
else: else:
now = time.time() now = time.time()
if now < on_interface.announce_allowed_at: if now < on_interface.announce_allowed_at:
RNS.log("Blocking recursive path request on "+str(on_interface)+" due to active announce cap", RNS.LOG_EXTREME) RNS.log(f"Blocking recursive path request on {on_interface} due to active announce cap", RNS.LOG_EXTREME)
return return
else: else:
tx_time = ((len(path_request_data)+RNS.Reticulum.HEADER_MINSIZE)*8) / on_interface.bitrate tx_time = ((len(path_request_data)+RNS.Reticulum.HEADER_MINSIZE)*8) / on_interface.bitrate
@ -2310,8 +2319,8 @@ class Transport:
return response return response
except Exception as e: except Exception as e:
RNS.log("An error occurred while processing remote status request from "+str(remote_identity), RNS.LOG_ERROR) RNS.log(f"An error occurred while processing remote status request from {remote_identity}", RNS.LOG_ERROR)
RNS.log("The contained exception was: "+str(e), RNS.LOG_ERROR) RNS.log(f"The contained exception was: {e}", RNS.LOG_ERROR)
return None return None
@ -2346,8 +2355,8 @@ class Transport:
return response return response
except Exception as e: except Exception as e:
RNS.log("An error occurred while processing remote status request from "+str(remote_identity), RNS.LOG_ERROR) RNS.log(f"An error occurred while processing remote status request from {remote_identity}", RNS.LOG_ERROR)
RNS.log("The contained exception was: "+str(e), RNS.LOG_ERROR) RNS.log(f"The contained exception was: {e}", RNS.LOG_ERROR)
return None return None
@ -2393,13 +2402,13 @@ class Transport:
) )
else: else:
RNS.log("Ignoring duplicate path request for "+RNS.prettyhexrep(destination_hash)+" with tag "+RNS.prettyhexrep(unique_tag), RNS.LOG_DEBUG) RNS.log(f"Ignoring duplicate path request for {RNS.prettyhexrep(destination_hash)} with tag {RNS.prettyhexrep(unique_tag)}", RNS.LOG_DEBUG)
else: else:
RNS.log("Ignoring tagless path request for "+RNS.prettyhexrep(destination_hash), RNS.LOG_DEBUG) RNS.log(f"Ignoring tagless path request for {RNS.prettyhexrep(destination_hash)}", RNS.LOG_DEBUG)
except Exception as e: except Exception as e:
RNS.log("Error while handling path request. The contained exception was: "+str(e), RNS.LOG_ERROR) RNS.log(f"Error while handling path request. The contained exception was: {e}", RNS.LOG_ERROR)
@staticmethod @staticmethod
def path_request(destination_hash, is_from_local_client, attached_interface, requestor_transport_id=None, tag=None): def path_request(destination_hash, is_from_local_client, attached_interface, requestor_transport_id=None, tag=None):
@ -2409,11 +2418,11 @@ class Transport:
if RNS.Reticulum.transport_enabled() and attached_interface.mode in RNS.Interfaces.Interface.Interface.DISCOVER_PATHS_FOR: if RNS.Reticulum.transport_enabled() and attached_interface.mode in RNS.Interfaces.Interface.Interface.DISCOVER_PATHS_FOR:
should_search_for_unknown = True should_search_for_unknown = True
interface_str = " on "+str(attached_interface) interface_str = f" on {attached_interface}"
else: else:
interface_str = "" interface_str = ""
RNS.log("Path request for "+RNS.prettyhexrep(destination_hash)+interface_str, RNS.LOG_DEBUG) RNS.log(f"Path request for {RNS.prettyhexrep(destination_hash)}{interface_str}", RNS.LOG_DEBUG)
destination_exists_on_local_client = False destination_exists_on_local_client = False
if len(Transport.local_client_interfaces) > 0: if len(Transport.local_client_interfaces) > 0:
@ -2427,7 +2436,7 @@ class Transport:
local_destination = next((d for d in Transport.destinations if d.hash == destination_hash), None) local_destination = next((d for d in Transport.destinations if d.hash == destination_hash), None)
if local_destination != None: if local_destination != None:
local_destination.announce(path_response=True, tag=tag, attached_interface=attached_interface) local_destination.announce(path_response=True, tag=tag, attached_interface=attached_interface)
RNS.log("Answering path request for "+RNS.prettyhexrep(destination_hash)+interface_str+", destination is local to this system", RNS.LOG_DEBUG) RNS.log(f"Answering path request for {RNS.prettyhexrep(destination_hash)}{interface_str}, destination is local to this system", RNS.LOG_DEBUG)
elif (RNS.Reticulum.transport_enabled() or is_from_local_client) and (destination_hash in Transport.destination_table): elif (RNS.Reticulum.transport_enabled() or is_from_local_client) and (destination_hash in Transport.destination_table):
packet = Transport.destination_table[destination_hash][6] packet = Transport.destination_table[destination_hash][6]
@ -2445,9 +2454,9 @@ class Transport:
# inefficient. There is probably a better way. Doing # inefficient. There is probably a better way. Doing
# path invalidation here would decrease the network # path invalidation here would decrease the network
# convergence time. Maybe just drop it? # convergence time. Maybe just drop it?
RNS.log("Not answering path request for "+RNS.prettyhexrep(destination_hash)+interface_str+", since next hop is the requestor", RNS.LOG_DEBUG) RNS.log(f"Not answering path request for {RNS.prettyhexrep(destination_hash)}{interface_str}, since next hop is the requestor", RNS.LOG_DEBUG)
else: else:
RNS.log("Answering path request for "+RNS.prettyhexrep(destination_hash)+interface_str+", path is known", RNS.LOG_DEBUG) RNS.log(f"Answering path request for {RNS.prettyhexrep(destination_hash)}{interface_str}, path is known", RNS.LOG_DEBUG)
now = time.time() now = time.time()
retries = Transport.PATHFINDER_R retries = Transport.PATHFINDER_R
@ -2459,7 +2468,7 @@ class Transport:
retransmit_timeout = now retransmit_timeout = now
else: else:
if Transport.is_local_client_interface(Transport.next_hop_interface(destination_hash)): if Transport.is_local_client_interface(Transport.next_hop_interface(destination_hash)):
RNS.log("Path request destination "+RNS.prettyhexrep(destination_hash)+" is on a local client interface, rebroadcasting immediately", RNS.LOG_EXTREME) RNS.log(f"Path request destination {RNS.prettyhexrep(destination_hash)} is on a local client interface, rebroadcasting immediately", RNS.LOG_EXTREME)
retransmit_timeout = now retransmit_timeout = now
else: else:
@ -2486,7 +2495,7 @@ class Transport:
elif is_from_local_client: elif is_from_local_client:
# Forward path request on all interfaces # Forward path request on all interfaces
# except the local client # except the local client
RNS.log("Forwarding path request from local client for "+RNS.prettyhexrep(destination_hash)+interface_str+" to all other interfaces", RNS.LOG_DEBUG) RNS.log(f"Forwarding path request from local client for {RNS.prettyhexrep(destination_hash)}{interface_str} to all other interfaces", RNS.LOG_DEBUG)
request_tag = RNS.Identity.get_random_hash() request_tag = RNS.Identity.get_random_hash()
for interface in Transport.interfaces: for interface in Transport.interfaces:
if not interface == attached_interface: if not interface == attached_interface:
@ -2494,11 +2503,11 @@ class Transport:
elif should_search_for_unknown: elif should_search_for_unknown:
if destination_hash in Transport.discovery_path_requests: if destination_hash in Transport.discovery_path_requests:
RNS.log("There is already a waiting path request for "+RNS.prettyhexrep(destination_hash)+" on behalf of path request"+interface_str, RNS.LOG_DEBUG) RNS.log(f"There is already a waiting path request for {RNS.prettyhexrep(destination_hash)} on behalf of path request{interface_str}", RNS.LOG_DEBUG)
else: else:
# Forward path request on all interfaces # Forward path request on all interfaces
# except the requestor interface # except the requestor interface
RNS.log("Attempting to discover unknown path to "+RNS.prettyhexrep(destination_hash)+" on behalf of path request"+interface_str, RNS.LOG_DEBUG) RNS.log(f"Attempting to discover unknown path to {RNS.prettyhexrep(destination_hash)} on behalf of path request{interface_str}", RNS.LOG_DEBUG)
pr_entry = { "destination_hash": destination_hash, "timeout": time.time()+Transport.PATH_REQUEST_TIMEOUT, "requesting_interface": attached_interface } pr_entry = { "destination_hash": destination_hash, "timeout": time.time()+Transport.PATH_REQUEST_TIMEOUT, "requesting_interface": attached_interface }
Transport.discovery_path_requests[destination_hash] = pr_entry Transport.discovery_path_requests[destination_hash] = pr_entry
@ -2511,12 +2520,12 @@ class Transport:
elif not is_from_local_client and len(Transport.local_client_interfaces) > 0: elif not is_from_local_client and len(Transport.local_client_interfaces) > 0:
# Forward the path request on all local # Forward the path request on all local
# client interfaces # client interfaces
RNS.log("Forwarding path request for "+RNS.prettyhexrep(destination_hash)+interface_str+" to local clients", RNS.LOG_DEBUG) RNS.log(f"Forwarding path request for {RNS.prettyhexrep(destination_hash)}{interface_str} to local clients", RNS.LOG_DEBUG)
for interface in Transport.local_client_interfaces: for interface in Transport.local_client_interfaces:
Transport.request_path(destination_hash, on_interface=interface) Transport.request_path(destination_hash, on_interface=interface)
else: else:
RNS.log("Ignoring path request for "+RNS.prettyhexrep(destination_hash)+interface_str+", no path known", RNS.LOG_DEBUG) RNS.log(f"Ignoring path request for {RNS.prettyhexrep(destination_hash)}{interface_str}, no path known", RNS.LOG_DEBUG)
@staticmethod @staticmethod
def from_local_client(packet): def from_local_client(packet):
@ -2568,7 +2577,7 @@ class Transport:
try: try:
interface.detach() interface.detach()
except Exception as e: except Exception as e:
RNS.log("An error occurred while detaching "+str(interface)+". The contained exception was: "+str(e), RNS.LOG_ERROR) RNS.log(f"An error occurred while detaching {interface}. The contained exception was: {e}", RNS.LOG_ERROR)
@staticmethod @staticmethod
def shared_connection_disappeared(): def shared_connection_disappeared():
@ -2604,10 +2613,10 @@ class Transport:
if na == 1: if na == 1:
na_str = "1 announce" na_str = "1 announce"
else: else:
na_str = str(na)+" announces" na_str = f"{na} announces"
interface.announce_queue = [] interface.announce_queue = []
RNS.log("Dropped "+na_str+" on "+str(interface), RNS.LOG_VERBOSE) RNS.log(f"Dropped {na_str} on {interface}", RNS.LOG_VERBOSE)
@staticmethod @staticmethod
@ -2640,20 +2649,20 @@ class Transport:
else: else:
RNS.log("Saving packet hashlist to storage...", RNS.LOG_DEBUG) RNS.log("Saving packet hashlist to storage...", RNS.LOG_DEBUG)
packet_hashlist_path = RNS.Reticulum.storagepath+"/packet_hashlist" packet_hashlist_path = f"{RNS.Reticulum.storagepath}/packet_hashlist"
file = open(packet_hashlist_path, "wb") file = open(packet_hashlist_path, "wb")
file.write(umsgpack.packb(Transport.packet_hashlist)) file.write(umsgpack.packb(Transport.packet_hashlist))
file.close() file.close()
save_time = time.time() - save_start save_time = time.time() - save_start
if save_time < 1: if save_time < 1:
time_str = str(round(save_time*1000,2))+"ms" time_str = f"{round(save_time * 1000, 2)}ms"
else: else:
time_str = str(round(save_time,2))+"s" time_str = f"{round(save_time, 2)}s"
RNS.log("Saved packet hashlist in "+time_str, RNS.LOG_DEBUG) RNS.log(f"Saved packet hashlist in {time_str}", RNS.LOG_DEBUG)
except Exception as e: except Exception as e:
RNS.log("Could not save packet hashlist to storage, the contained exception was: "+str(e), RNS.LOG_ERROR) RNS.log(f"Could not save packet hashlist to storage, the contained exception was: {e}", RNS.LOG_ERROR)
Transport.saving_packet_hashlist = False Transport.saving_packet_hashlist = False
@ -2710,20 +2719,20 @@ class Transport:
Transport.cache(de[6], force_cache=True) Transport.cache(de[6], force_cache=True)
destination_table_path = RNS.Reticulum.storagepath+"/destination_table" destination_table_path = f"{RNS.Reticulum.storagepath}/destination_table"
file = open(destination_table_path, "wb") file = open(destination_table_path, "wb")
file.write(umsgpack.packb(serialised_destinations)) file.write(umsgpack.packb(serialised_destinations))
file.close() file.close()
save_time = time.time() - save_start save_time = time.time() - save_start
if save_time < 1: if save_time < 1:
time_str = str(round(save_time*1000,2))+"ms" time_str = f"{round(save_time * 1000, 2)}ms"
else: else:
time_str = str(round(save_time,2))+"s" time_str = f"{round(save_time, 2)}s"
RNS.log("Saved "+str(len(serialised_destinations))+" path table entries in "+time_str, RNS.LOG_DEBUG) RNS.log(f"Saved {len(serialised_destinations)} path table entries in {time_str}", RNS.LOG_DEBUG)
except Exception as e: except Exception as e:
RNS.log("Could not save path table to storage, the contained exception was: "+str(e), RNS.LOG_ERROR) RNS.log(f"Could not save path table to storage, the contained exception was: {e}", RNS.LOG_ERROR)
Transport.saving_path_table = False Transport.saving_path_table = False
@ -2788,19 +2797,19 @@ class Transport:
serialised_tunnel = [tunnel_id, interface_hash, serialised_paths, expires] serialised_tunnel = [tunnel_id, interface_hash, serialised_paths, expires]
serialised_tunnels.append(serialised_tunnel) serialised_tunnels.append(serialised_tunnel)
tunnels_path = RNS.Reticulum.storagepath+"/tunnels" tunnels_path = f"{RNS.Reticulum.storagepath}/tunnels"
file = open(tunnels_path, "wb") file = open(tunnels_path, "wb")
file.write(umsgpack.packb(serialised_tunnels)) file.write(umsgpack.packb(serialised_tunnels))
file.close() file.close()
save_time = time.time() - save_start save_time = time.time() - save_start
if save_time < 1: if save_time < 1:
time_str = str(round(save_time*1000,2))+"ms" time_str = f"{round(save_time * 1000, 2)}ms"
else: else:
time_str = str(round(save_time,2))+"s" time_str = f"{round(save_time, 2)}s"
RNS.log("Saved "+str(len(serialised_tunnels))+" tunnel table entries in "+time_str, RNS.LOG_DEBUG) RNS.log(f"Saved {len(serialised_tunnels)} tunnel table entries in {time_str}", RNS.LOG_DEBUG)
except Exception as e: except Exception as e:
RNS.log("Could not save tunnel table to storage, the contained exception was: "+str(e), RNS.LOG_ERROR) RNS.log(f"Could not save tunnel table to storage, the contained exception was: {e}", RNS.LOG_ERROR)
Transport.saving_tunnel_table = False Transport.saving_tunnel_table = False

View File

@ -23,5 +23,5 @@
import os import os
import glob import glob
modules = glob.glob(os.path.dirname(__file__)+"/*.py") modules = glob.glob(f"{os.path.dirname(__file__)}/*.py")
__all__ = [ os.path.basename(f)[:-3] for f in modules if not f.endswith('__init__.py')] __all__ = [ os.path.basename(f)[:-3] for f in modules if not f.endswith('__init__.py')]

View File

@ -35,6 +35,7 @@ APP_NAME = "rncp"
allow_all = False allow_all = False
allow_fetch = False allow_fetch = False
fetch_jail = None fetch_jail = None
save_path = None
show_phy_rates = False show_phy_rates = False
allowed_identity_hashes = [] allowed_identity_hashes = []
@ -44,8 +45,8 @@ es = " "
erase_str = "\33[2K\r" erase_str = "\33[2K\r"
def listen(configdir, verbosity = 0, quietness = 0, allowed = [], display_identity = False, def listen(configdir, verbosity = 0, quietness = 0, allowed = [], display_identity = False,
limit = None, disable_auth = None, fetch_allowed = False, jail = None, announce = False): limit = None, disable_auth = None, fetch_allowed = False, jail = None, save = None, announce = False):
global allow_all, allow_fetch, allowed_identity_hashes, fetch_jail global allow_all, allow_fetch, allowed_identity_hashes, fetch_jail, save_path
from tempfile import TemporaryFile from tempfile import TemporaryFile
allow_fetch = fetch_allowed allow_fetch = fetch_allowed
@ -58,9 +59,23 @@ def listen(configdir, verbosity = 0, quietness = 0, allowed = [], display_identi
if jail != None: if jail != None:
fetch_jail = os.path.abspath(os.path.expanduser(jail)) fetch_jail = os.path.abspath(os.path.expanduser(jail))
RNS.log("Restricting fetch requests to paths under \""+fetch_jail+"\"", RNS.LOG_VERBOSE) RNS.log(f"Restricting fetch requests to paths under \"{fetch_jail}\"", RNS.LOG_VERBOSE)
identity_path = RNS.Reticulum.identitypath+"/"+APP_NAME if save != None:
sp = os.path.abspath(os.path.expanduser(save))
if os.path.isdir(sp):
if os.access(sp, os.W_OK):
save_path = sp
else:
RNS.log("Output directory not writable", RNS.LOG_ERROR)
exit(4)
else:
RNS.log("Output directory not found", RNS.LOG_ERROR)
exit(3)
RNS.log(f"Saving received files in \"{save_path}\"", RNS.LOG_VERBOSE)
identity_path = f"{RNS.Reticulum.identitypath}/{APP_NAME}"
if os.path.isfile(identity_path): if os.path.isfile(identity_path):
identity = RNS.Identity.from_file(identity_path) identity = RNS.Identity.from_file(identity_path)
@ -72,8 +87,8 @@ def listen(configdir, verbosity = 0, quietness = 0, allowed = [], display_identi
destination = RNS.Destination(identity, RNS.Destination.IN, RNS.Destination.SINGLE, APP_NAME, "receive") destination = RNS.Destination(identity, RNS.Destination.IN, RNS.Destination.SINGLE, APP_NAME, "receive")
if display_identity: if display_identity:
print("Identity : "+str(identity)) print(f"Identity : {identity}")
print("Listening on : "+RNS.prettyhexrep(destination.hash)) print(f"Listening on : {RNS.prettyhexrep(destination.hash)}")
exit(0) exit(0)
if disable_auth: if disable_auth:
@ -83,14 +98,14 @@ def listen(configdir, verbosity = 0, quietness = 0, allowed = [], display_identi
try: try:
allowed_file_name = "allowed_identities" allowed_file_name = "allowed_identities"
allowed_file = None allowed_file = None
if os.path.isfile(os.path.expanduser("/etc/rncp/"+allowed_file_name)): if os.path.isfile(os.path.expanduser(f"/etc/rncp/{allowed_file_name}")):
allowed_file = os.path.expanduser("/etc/rncp/"+allowed_file_name) allowed_file = os.path.expanduser(f"/etc/rncp/{allowed_file_name}")
elif os.path.isfile(os.path.expanduser("~/.config/rncp/"+allowed_file_name)): elif os.path.isfile(os.path.expanduser(f"~/.config/rncp/{allowed_file_name}")):
allowed_file = os.path.expanduser("~/.config/rncp/"+allowed_file_name) allowed_file = os.path.expanduser(f"~/.config/rncp/{allowed_file_name}")
elif os.path.isfile(os.path.expanduser("~/.rncp/"+allowed_file_name)): elif os.path.isfile(os.path.expanduser(f"~/.rncp/{allowed_file_name}")):
allowed_file = os.path.expanduser("~/.rncp/"+allowed_file_name) allowed_file = os.path.expanduser(f"~/.rncp/{allowed_file_name}")
if allowed_file != None: if allowed_file != None:
af = open(allowed_file, "r") af = open(allowed_file)
al = af.read().replace("\r", "").split("\n") al = af.read().replace("\r", "").split("\n")
ali = [] ali = []
for a in al: for a in al:
@ -107,16 +122,16 @@ def listen(configdir, verbosity = 0, quietness = 0, allowed = [], display_identi
else: else:
ms = "ies" ms = "ies"
RNS.log("Loaded "+str(len(ali))+" allowed identit"+ms+" from "+str(allowed_file), RNS.LOG_VERBOSE) RNS.log(f"Loaded {len(ali)} allowed identit{ms} from {allowed_file}", RNS.LOG_VERBOSE)
except Exception as e: except Exception as e:
RNS.log("Error while parsing allowed_identities file. The contained exception was: "+str(e), RNS.LOG_ERROR) RNS.log(f"Error while parsing allowed_identities file. The contained exception was: {e}", RNS.LOG_ERROR)
if allowed != None: if allowed != None:
for a in allowed: for a in allowed:
try: try:
if len(a) != dest_len: if len(a) != dest_len:
raise ValueError("Allowed destination length is invalid, must be {hex} hexadecimal characters ({byte} bytes).".format(hex=dest_len, byte=dest_len//2)) raise ValueError(f"Allowed destination length is invalid, must be {dest_len} hexadecimal characters ({dest_len // 2} bytes).")
try: try:
destination_hash = bytes.fromhex(a) destination_hash = bytes.fromhex(a)
allowed_identity_hashes.append(destination_hash) allowed_identity_hashes.append(destination_hash)
@ -135,9 +150,11 @@ def listen(configdir, verbosity = 0, quietness = 0, allowed = [], display_identi
return REQ_FETCH_NOT_ALLOWED return REQ_FETCH_NOT_ALLOWED
if fetch_jail: if fetch_jail:
if data.startswith(fetch_jail+"/"):
data = data.replace(fetch_jail+"/", "")
file_path = os.path.abspath(os.path.expanduser(f"{fetch_jail}/{data}")) file_path = os.path.abspath(os.path.expanduser(f"{fetch_jail}/{data}"))
RNS.log(f"Disallowing fetch request for {file_path} outside of fetch jail {fetch_jail}", RNS.LOG_WARNING) if not file_path.startswith(fetch_jail+"/"):
if not file_path.startswith(fetch_jail): RNS.log(f"Disallowing fetch request for {file_path} outside of fetch jail {fetch_jail}", RNS.LOG_WARNING)
return REQ_FETCH_NOT_ALLOWED return REQ_FETCH_NOT_ALLOWED
else: else:
file_path = os.path.abspath(os.path.expanduser(f"{data}")) file_path = os.path.abspath(os.path.expanduser(f"{data}"))
@ -148,11 +165,11 @@ def listen(configdir, verbosity = 0, quietness = 0, allowed = [], display_identi
target_link = link target_link = link
if not os.path.isfile(file_path): if not os.path.isfile(file_path):
RNS.log("Client-requested file not found: "+str(file_path), RNS.LOG_VERBOSE) RNS.log(f"Client-requested file not found: {file_path}", RNS.LOG_VERBOSE)
return False return False
else: else:
if target_link != None: if target_link != None:
RNS.log("Sending file "+str(file_path)+" to client", RNS.LOG_VERBOSE) RNS.log(f"Sending file {file_path} to client", RNS.LOG_VERBOSE)
temp_file = TemporaryFile() temp_file = TemporaryFile()
real_file = open(file_path, "rb") real_file = open(file_path, "rb")
@ -182,7 +199,7 @@ def listen(configdir, verbosity = 0, quietness = 0, allowed = [], display_identi
else: else:
destination.register_request_handler("fetch_file", response_generator=fetch_request, allow=RNS.Destination.ALLOW_LIST, allowed_list=allowed_identity_hashes) destination.register_request_handler("fetch_file", response_generator=fetch_request, allow=RNS.Destination.ALLOW_LIST, allowed_list=allowed_identity_hashes)
print("rncp listening on "+RNS.prettyhexrep(destination.hash)) print(f"rncp listening on {RNS.prettyhexrep(destination.hash)}")
if announce >= 0: if announce >= 0:
def job(): def job():
@ -233,25 +250,25 @@ def receive_resource_callback(resource):
def receive_resource_started(resource): def receive_resource_started(resource):
if resource.link.get_remote_identity(): if resource.link.get_remote_identity():
id_str = " from "+RNS.prettyhexrep(resource.link.get_remote_identity().hash) id_str = f" from {RNS.prettyhexrep(resource.link.get_remote_identity().hash)}"
else: else:
id_str = "" id_str = ""
print("Starting resource transfer "+RNS.prettyhexrep(resource.hash)+id_str) print(f"Starting resource transfer {RNS.prettyhexrep(resource.hash)}{id_str}")
def receive_resource_concluded(resource): def receive_resource_concluded(resource):
global fetch_jail global save_path
if resource.status == RNS.Resource.COMPLETE: if resource.status == RNS.Resource.COMPLETE:
print(str(resource)+" completed") print(f"{resource} completed")
if resource.total_size > 4: if resource.total_size > 4:
filename_len = int.from_bytes(resource.data.read(2), "big") filename_len = int.from_bytes(resource.data.read(2), "big")
filename = resource.data.read(filename_len).decode("utf-8") filename = resource.data.read(filename_len).decode("utf-8")
counter = 0 counter = 0
if fetch_jail: if save_path:
saved_filename = os.path.abspath(os.path.expanduser(fetch_jail+"/"+filename)) saved_filename = os.path.abspath(os.path.expanduser(save_path+"/"+filename))
if not saved_filename.startswith(fetch_jail): if not saved_filename.startswith(save_path+"/"):
RNS.log(f"Invalid save path {saved_filename}, ignoring", RNS.LOG_ERROR) RNS.log(f"Invalid save path {saved_filename}, ignoring", RNS.LOG_ERROR)
return return
else: else:
@ -260,7 +277,7 @@ def receive_resource_concluded(resource):
full_save_path = saved_filename full_save_path = saved_filename
while os.path.isfile(full_save_path): while os.path.isfile(full_save_path):
counter += 1 counter += 1
full_save_path = saved_filename+"."+str(counter) full_save_path = f"{saved_filename}.{counter}"
file = open(full_save_path, "wb") file = open(full_save_path, "wb")
file.write(resource.data.read()) file.write(resource.data.read())
@ -311,15 +328,27 @@ def sender_progress(resource):
resource_done = True resource_done = True
link = None link = None
def fetch(configdir, verbosity = 0, quietness = 0, destination = None, file = None, timeout = RNS.Transport.PATH_REQUEST_TIMEOUT, silent=False, phy_rates=False): def fetch(configdir, verbosity = 0, quietness = 0, destination = None, file = None, timeout = RNS.Transport.PATH_REQUEST_TIMEOUT, silent=False, phy_rates=False, save=None):
global current_resource, resource_done, link, speed, show_phy_rates global current_resource, resource_done, link, speed, show_phy_rates, save_path
targetloglevel = 3+verbosity-quietness targetloglevel = 3+verbosity-quietness
show_phy_rates = phy_rates show_phy_rates = phy_rates
if save:
sp = os.path.abspath(os.path.expanduser(save))
if os.path.isdir(sp):
if os.access(sp, os.W_OK):
save_path = sp
else:
RNS.log("Output directory not writable", RNS.LOG_ERROR)
exit(4)
else:
RNS.log("Output directory not found", RNS.LOG_ERROR)
exit(3)
try: try:
dest_len = (RNS.Reticulum.TRUNCATED_HASHLENGTH//8)*2 dest_len = (RNS.Reticulum.TRUNCATED_HASHLENGTH//8)*2
if len(destination) != dest_len: if len(destination) != dest_len:
raise ValueError("Allowed destination length is invalid, must be {hex} hexadecimal characters ({byte} bytes).".format(hex=dest_len, byte=dest_len//2)) raise ValueError(f"Allowed destination length is invalid, must be {dest_len} hexadecimal characters ({dest_len // 2} bytes).")
try: try:
destination_hash = bytes.fromhex(destination) destination_hash = bytes.fromhex(destination)
except Exception as e: except Exception as e:
@ -330,11 +359,11 @@ def fetch(configdir, verbosity = 0, quietness = 0, destination = None, file = No
reticulum = RNS.Reticulum(configdir=configdir, loglevel=targetloglevel) reticulum = RNS.Reticulum(configdir=configdir, loglevel=targetloglevel)
identity_path = RNS.Reticulum.identitypath+"/"+APP_NAME identity_path = f"{RNS.Reticulum.identitypath}/{APP_NAME}"
if os.path.isfile(identity_path): if os.path.isfile(identity_path):
identity = RNS.Identity.from_file(identity_path) identity = RNS.Identity.from_file(identity_path)
if identity == None: if identity == None:
RNS.log("Could not load identity for rncp. The identity file at \""+str(identity_path)+"\" may be corrupt or unreadable.", RNS.LOG_ERROR) RNS.log(f"Could not load identity for rncp. The identity file at \"{identity_path}\" may be corrupt or unreadable.", RNS.LOG_ERROR)
exit(2) exit(2)
else: else:
identity = None identity = None
@ -347,9 +376,9 @@ def fetch(configdir, verbosity = 0, quietness = 0, destination = None, file = No
if not RNS.Transport.has_path(destination_hash): if not RNS.Transport.has_path(destination_hash):
RNS.Transport.request_path(destination_hash) RNS.Transport.request_path(destination_hash)
if silent: if silent:
print("Path to "+RNS.prettyhexrep(destination_hash)+" requested") print(f"Path to {RNS.prettyhexrep(destination_hash)} requested")
else: else:
print("Path to "+RNS.prettyhexrep(destination_hash)+" requested ", end=es) print(f"Path to {RNS.prettyhexrep(destination_hash)} requested ", end=es)
sys.stdout.flush() sys.stdout.flush()
i = 0 i = 0
@ -358,7 +387,7 @@ def fetch(configdir, verbosity = 0, quietness = 0, destination = None, file = No
while not RNS.Transport.has_path(destination_hash) and time.time() < estab_timeout: while not RNS.Transport.has_path(destination_hash) and time.time() < estab_timeout:
if not silent: if not silent:
time.sleep(0.1) time.sleep(0.1)
print(("\b\b"+syms[i]+" "), end="") print(f"\b\b{syms[i]} ", end="")
sys.stdout.flush() sys.stdout.flush()
i = (i+1)%len(syms) i = (i+1)%len(syms)
@ -370,9 +399,9 @@ def fetch(configdir, verbosity = 0, quietness = 0, destination = None, file = No
exit(1) exit(1)
else: else:
if silent: if silent:
print("Establishing link with "+RNS.prettyhexrep(destination_hash)) print(f"Establishing link with {RNS.prettyhexrep(destination_hash)}")
else: else:
print(f"{erase_str}Establishing link with "+RNS.prettyhexrep(destination_hash)+" ", end=es) print(f"{erase_str}Establishing link with {RNS.prettyhexrep(destination_hash)} ", end=es)
listener_identity = RNS.Identity.recall(destination_hash) listener_identity = RNS.Identity.recall(destination_hash)
listener_destination = RNS.Destination( listener_destination = RNS.Destination(
@ -387,15 +416,15 @@ def fetch(configdir, verbosity = 0, quietness = 0, destination = None, file = No
while link.status != RNS.Link.ACTIVE and time.time() < estab_timeout: while link.status != RNS.Link.ACTIVE and time.time() < estab_timeout:
if not silent: if not silent:
time.sleep(0.1) time.sleep(0.1)
print(("\b\b"+syms[i]+" "), end="") print(f"\b\b{syms[i]} ", end="")
sys.stdout.flush() sys.stdout.flush()
i = (i+1)%len(syms) i = (i+1)%len(syms)
if not RNS.Transport.has_path(destination_hash): if not RNS.Transport.has_path(destination_hash):
if silent: if silent:
print("Could not establish link with "+RNS.prettyhexrep(destination_hash)) print(f"Could not establish link with {RNS.prettyhexrep(destination_hash)}")
else: else:
print(f"{erase_str}Could not establish link with "+RNS.prettyhexrep(destination_hash)) print(f"{erase_str}Could not establish link with {RNS.prettyhexrep(destination_hash)}")
exit(1) exit(1)
else: else:
if silent: if silent:
@ -436,18 +465,25 @@ def fetch(configdir, verbosity = 0, quietness = 0, destination = None, file = No
def fetch_resource_concluded(resource): def fetch_resource_concluded(resource):
nonlocal resource_resolved, resource_status nonlocal resource_resolved, resource_status
global save_path
if resource.status == RNS.Resource.COMPLETE: if resource.status == RNS.Resource.COMPLETE:
if resource.total_size > 4: if resource.total_size > 4:
filename_len = int.from_bytes(resource.data.read(2), "big") filename_len = int.from_bytes(resource.data.read(2), "big")
filename = resource.data.read(filename_len).decode("utf-8") filename = resource.data.read(filename_len).decode("utf-8")
counter = 0 counter = 0
saved_filename = filename if save_path:
while os.path.isfile(saved_filename): saved_filename = os.path.abspath(os.path.expanduser(save_path+"/"+filename))
counter += 1
saved_filename = filename+"."+str(counter)
file = open(saved_filename, "wb") else:
saved_filename = filename
full_save_path = saved_filename
while os.path.isfile(full_save_path):
counter += 1
full_save_path = f"{saved_filename}.{counter)"
file = open(full_save_path, "wb")
file.write(resource.data.read()) file.write(resource.data.read())
file.close() file.close()
resource_status = "completed" resource_status = "completed"
@ -471,19 +507,19 @@ def fetch(configdir, verbosity = 0, quietness = 0, destination = None, file = No
while not request_resolved: while not request_resolved:
if not silent: if not silent:
time.sleep(0.1) time.sleep(0.1)
print(("\b\b"+syms[i]+" "), end="") print(f"\b\b{syms[i]} ", end="")
sys.stdout.flush() sys.stdout.flush()
i = (i+1)%len(syms) i = (i+1)%len(syms)
if request_status == "fetch_not_allowed": if request_status == "fetch_not_allowed":
if not silent: print(f"{erase_str}", end="") if not silent: print(f"{erase_str}", end="")
print("Fetch request failed, fetching the file "+str(file)+" was not allowed by the remote") print(f"Fetch request failed, fetching the file {file} was not allowed by the remote")
link.teardown() link.teardown()
time.sleep(0.15) time.sleep(0.15)
exit(0) exit(0)
elif request_status == "not_found": elif request_status == "not_found":
if not silent: print(f"{erase_str}", end="") if not silent: print(f"{erase_str}", end="")
print("Fetch request failed, the file "+str(file)+" was not found on the remote") print(f"Fetch request failed, the file {file} was not found on the remote")
link.teardown() link.teardown()
time.sleep(0.15) time.sleep(0.15)
exit(0) exit(0)
@ -534,9 +570,9 @@ def fetch(configdir, verbosity = 0, quietness = 0, destination = None, file = No
exit(1) exit(1)
else: else:
if silent: if silent:
print(str(file)+" fetched from "+RNS.prettyhexrep(destination_hash)) print(f"{file} fetched from {RNS.prettyhexrep(destination_hash)}")
else: else:
print("\n"+str(file)+" fetched from "+RNS.prettyhexrep(destination_hash)) print(f"\n{file} fetched from {RNS.prettyhexrep(destination_hash)}")
link.teardown() link.teardown()
time.sleep(0.15) time.sleep(0.15)
exit(0) exit(0)
@ -554,7 +590,7 @@ def send(configdir, verbosity = 0, quietness = 0, destination = None, file = Non
try: try:
dest_len = (RNS.Reticulum.TRUNCATED_HASHLENGTH//8)*2 dest_len = (RNS.Reticulum.TRUNCATED_HASHLENGTH//8)*2
if len(destination) != dest_len: if len(destination) != dest_len:
raise ValueError("Allowed destination length is invalid, must be {hex} hexadecimal characters ({byte} bytes).".format(hex=dest_len, byte=dest_len//2)) raise ValueError(f"Allowed destination length is invalid, must be {dest_len} hexadecimal characters ({dest_len // 2} bytes).")
try: try:
destination_hash = bytes.fromhex(destination) destination_hash = bytes.fromhex(destination)
except Exception as e: except Exception as e:
@ -589,11 +625,11 @@ def send(configdir, verbosity = 0, quietness = 0, destination = None, file = Non
reticulum = RNS.Reticulum(configdir=configdir, loglevel=targetloglevel) reticulum = RNS.Reticulum(configdir=configdir, loglevel=targetloglevel)
identity_path = RNS.Reticulum.identitypath+"/"+APP_NAME identity_path = f"{RNS.Reticulum.identitypath}/{APP_NAME}"
if os.path.isfile(identity_path): if os.path.isfile(identity_path):
identity = RNS.Identity.from_file(identity_path) identity = RNS.Identity.from_file(identity_path)
if identity == None: if identity == None:
RNS.log("Could not load identity for rncp. The identity file at \""+str(identity_path)+"\" may be corrupt or unreadable.", RNS.LOG_ERROR) RNS.log(f"Could not load identity for rncp. The identity file at \"{identity_path}\" may be corrupt or unreadable.", RNS.LOG_ERROR)
exit(2) exit(2)
else: else:
identity = None identity = None
@ -606,9 +642,9 @@ def send(configdir, verbosity = 0, quietness = 0, destination = None, file = Non
if not RNS.Transport.has_path(destination_hash): if not RNS.Transport.has_path(destination_hash):
RNS.Transport.request_path(destination_hash) RNS.Transport.request_path(destination_hash)
if silent: if silent:
print("Path to "+RNS.prettyhexrep(destination_hash)+" requested") print(f"Path to {RNS.prettyhexrep(destination_hash)} requested")
else: else:
print("Path to "+RNS.prettyhexrep(destination_hash)+" requested ", end=es) print(f"Path to {RNS.prettyhexrep(destination_hash)} requested ", end=es)
sys.stdout.flush() sys.stdout.flush()
i = 0 i = 0
@ -617,7 +653,7 @@ def send(configdir, verbosity = 0, quietness = 0, destination = None, file = Non
while not RNS.Transport.has_path(destination_hash) and time.time() < estab_timeout: while not RNS.Transport.has_path(destination_hash) and time.time() < estab_timeout:
if not silent: if not silent:
time.sleep(0.1) time.sleep(0.1)
print(("\b\b"+syms[i]+" "), end="") print(f"\b\b{syms[i]} ", end="")
sys.stdout.flush() sys.stdout.flush()
i = (i+1)%len(syms) i = (i+1)%len(syms)
@ -629,9 +665,9 @@ def send(configdir, verbosity = 0, quietness = 0, destination = None, file = Non
exit(1) exit(1)
else: else:
if silent: if silent:
print("Establishing link with "+RNS.prettyhexrep(destination_hash)) print(f"Establishing link with {RNS.prettyhexrep(destination_hash)}")
else: else:
print(f"{erase_str}Establishing link with "+RNS.prettyhexrep(destination_hash)+" ", end=es) print(f"{erase_str}Establishing link with {RNS.prettyhexrep(destination_hash)} ", end=es)
receiver_identity = RNS.Identity.recall(destination_hash) receiver_identity = RNS.Identity.recall(destination_hash)
receiver_destination = RNS.Destination( receiver_destination = RNS.Destination(
@ -646,21 +682,21 @@ def send(configdir, verbosity = 0, quietness = 0, destination = None, file = Non
while link.status != RNS.Link.ACTIVE and time.time() < estab_timeout: while link.status != RNS.Link.ACTIVE and time.time() < estab_timeout:
if not silent: if not silent:
time.sleep(0.1) time.sleep(0.1)
print(("\b\b"+syms[i]+" "), end="") print(f"\b\b{syms[i]} ", end="")
sys.stdout.flush() sys.stdout.flush()
i = (i+1)%len(syms) i = (i+1)%len(syms)
if time.time() > estab_timeout: if time.time() > estab_timeout:
if silent: if silent:
print("Link establishment with "+RNS.prettyhexrep(destination_hash)+" timed out") print(f"Link establishment with {RNS.prettyhexrep(destination_hash)} timed out")
else: else:
print(f"{erase_str}Link establishment with "+RNS.prettyhexrep(destination_hash)+" timed out") print(f"{erase_str}Link establishment with {RNS.prettyhexrep(destination_hash)} timed out")
exit(1) exit(1)
elif not RNS.Transport.has_path(destination_hash): elif not RNS.Transport.has_path(destination_hash):
if silent: if silent:
print("No path found to "+RNS.prettyhexrep(destination_hash)) print(f"No path found to {RNS.prettyhexrep(destination_hash)}")
else: else:
print(f"{erase_str}No path found to "+RNS.prettyhexrep(destination_hash)) print(f"{erase_str}No path found to {RNS.prettyhexrep(destination_hash)}")
exit(1) exit(1)
else: else:
if silent: if silent:
@ -675,16 +711,16 @@ def send(configdir, verbosity = 0, quietness = 0, destination = None, file = Non
while resource.status < RNS.Resource.TRANSFERRING: while resource.status < RNS.Resource.TRANSFERRING:
if not silent: if not silent:
time.sleep(0.1) time.sleep(0.1)
print(("\b\b"+syms[i]+" "), end="") print(f"\b\b{syms[i]} ", end="")
sys.stdout.flush() sys.stdout.flush()
i = (i+1)%len(syms) i = (i+1)%len(syms)
if resource.status > RNS.Resource.COMPLETE: if resource.status > RNS.Resource.COMPLETE:
if silent: if silent:
print("File was not accepted by "+RNS.prettyhexrep(destination_hash)) print(f"File was not accepted by {RNS.prettyhexrep(destination_hash)}")
else: else:
print(f"{erase_str}File was not accepted by "+RNS.prettyhexrep(destination_hash)) print(f"{erase_str}File was not accepted by {RNS.prettyhexrep(destination_hash)}")
exit(1) exit(1)
else: else:
if silent: if silent:
@ -707,9 +743,9 @@ def send(configdir, verbosity = 0, quietness = 0, destination = None, file = Non
ss = size_str(speed, "b") ss = size_str(speed, "b")
stat_str = f"{percent}% - {cs} of {ts} - {ss}ps{phy_str}" stat_str = f"{percent}% - {cs} of {ts} - {ss}ps{phy_str}"
if not done: if not done:
print(f"{erase_str}Transferring file "+syms[i]+" "+stat_str, end=es) print(f"{erase_str}Transferring file {syms[i]} {stat_str}", end=es)
else: else:
print(f"{erase_str}Transfer complete "+stat_str, end=es) print(f"{erase_str}Transfer complete {stat_str}", end=es)
sys.stdout.flush() sys.stdout.flush()
i = (i+1)%len(syms) i = (i+1)%len(syms)
return i return i
@ -729,9 +765,9 @@ def send(configdir, verbosity = 0, quietness = 0, destination = None, file = Non
exit(1) exit(1)
else: else:
if silent: if silent:
print(str(file_path)+" copied to "+RNS.prettyhexrep(destination_hash)) print(f"{file_path} copied to {RNS.prettyhexrep(destination_hash)}")
else: else:
print("\n"+str(file_path)+" copied to "+RNS.prettyhexrep(destination_hash)) print(f"\n{file_path} copied to {RNS.prettyhexrep(destination_hash)}")
link.teardown() link.teardown()
time.sleep(0.25) time.sleep(0.25)
real_file.close() real_file.close()
@ -751,14 +787,15 @@ def main():
parser.add_argument("-F", '--allow-fetch', action='store_true', default=False, help="allow authenticated clients to fetch files") parser.add_argument("-F", '--allow-fetch', action='store_true', default=False, help="allow authenticated clients to fetch files")
parser.add_argument("-f", '--fetch', action='store_true', default=False, help="fetch file from remote listener instead of sending") parser.add_argument("-f", '--fetch', action='store_true', default=False, help="fetch file from remote listener instead of sending")
parser.add_argument("-j", "--jail", metavar="path", action="store", default=None, help="restrict fetch requests to specified path", type=str) parser.add_argument("-j", "--jail", metavar="path", action="store", default=None, help="restrict fetch requests to specified path", type=str)
parser.add_argument("-s", "--save", metavar="path", action="store", default=None, help="save received files in specified path", type=str)
parser.add_argument("-b", action='store', metavar="seconds", default=-1, help="announce interval, 0 to only announce at startup", type=int) parser.add_argument("-b", action='store', metavar="seconds", default=-1, help="announce interval, 0 to only announce at startup", type=int)
parser.add_argument('-a', metavar="allowed_hash", dest="allowed", action='append', help="allow this identity", type=str) parser.add_argument('-a', metavar="allowed_hash", dest="allowed", action='append', help="allow this identity (or add in ~/.rncp/allowed_identities)", type=str)
parser.add_argument('-n', '--no-auth', action='store_true', default=False, help="accept requests from anyone") parser.add_argument('-n', '--no-auth', action='store_true', default=False, help="accept requests from anyone")
parser.add_argument('-p', '--print-identity', action='store_true', default=False, help="print identity and destination info and exit") parser.add_argument('-p', '--print-identity', action='store_true', default=False, help="print identity and destination info and exit")
parser.add_argument("-w", action="store", metavar="seconds", type=float, help="sender timeout before giving up", default=RNS.Transport.PATH_REQUEST_TIMEOUT) parser.add_argument("-w", action="store", metavar="seconds", type=float, help="sender timeout before giving up", default=RNS.Transport.PATH_REQUEST_TIMEOUT)
parser.add_argument('-P', '--phy-rates', action='store_true', default=False, help="display physical layer transfer rates") parser.add_argument('-P', '--phy-rates', action='store_true', default=False, help="display physical layer transfer rates")
# parser.add_argument("--limit", action="store", metavar="files", type=float, help="maximum number of files to accept", default=None) # parser.add_argument("--limit", action="store", metavar="files", type=float, help="maximum number of files to accept", default=None)
parser.add_argument("--version", action="version", version="rncp {version}".format(version=__version__)) parser.add_argument("--version", action="version", version=f"rncp {__version__}")
args = parser.parse_args() args = parser.parse_args()
@ -770,6 +807,7 @@ def main():
allowed = args.allowed, allowed = args.allowed,
fetch_allowed = args.allow_fetch, fetch_allowed = args.allow_fetch,
jail = args.jail, jail = args.jail,
save = args.save,
display_identity=args.print_identity, display_identity=args.print_identity,
# limit=args.limit, # limit=args.limit,
disable_auth=args.no_auth, disable_auth=args.no_auth,
@ -787,6 +825,7 @@ def main():
timeout = args.w, timeout = args.w,
silent = args.silent, silent = args.silent,
phy_rates = args.phy_rates, phy_rates = args.phy_rates,
save = args.save,
) )
else: else:
print("") print("")
@ -830,12 +869,12 @@ def size_str(num, suffix='B'):
for unit in units: for unit in units:
if abs(num) < 1000.0: if abs(num) < 1000.0:
if unit == "": if unit == "":
return "%.0f %s%s" % (num, unit, suffix) return f"{num:.0f} {unit}{suffix}"
else: else:
return "%.2f %s%s" % (num, unit, suffix) return f"{num:.2f} {unit}{suffix}"
num /= 1000.0 num /= 1000.0
return "%.2f%s%s" % (num, last_unit, suffix) return f"{num:.2f}{last_unit}{suffix}"
if __name__ == "__main__": if __name__ == "__main__":
main() main()

View File

@ -43,14 +43,14 @@ def spin(until=None, msg=None, timeout=None):
if timeout != None: if timeout != None:
timeout = time.time()+timeout timeout = time.time()+timeout
print(msg+" ", end=" ") print(f"{msg} ", end=" ")
while (timeout == None or time.time()<timeout) and not until(): while (timeout == None or time.time()<timeout) and not until():
time.sleep(0.1) time.sleep(0.1)
print(("\b\b"+syms[i]+" "), end="") print(f"\b\b{syms[i]} ", end="")
sys.stdout.flush() sys.stdout.flush()
i = (i+1)%len(syms) i = (i+1)%len(syms)
print("\r"+" "*len(msg)+" \r", end="") print(f"\r{' ' * len(msg)} \r", end="")
if timeout != None and time.time() > timeout: if timeout != None and time.time() > timeout:
return False return False
@ -92,7 +92,7 @@ def main():
parser.add_argument("-b", "--base64", action="store_true", default=False, help="Use base64-encoded input and output") parser.add_argument("-b", "--base64", action="store_true", default=False, help="Use base64-encoded input and output")
parser.add_argument("-B", "--base32", action="store_true", default=False, help="Use base32-encoded input and output") parser.add_argument("-B", "--base32", action="store_true", default=False, help="Use base32-encoded input and output")
parser.add_argument("--version", action="version", version="rnid {version}".format(version=__version__)) parser.add_argument("--version", action="version", version=f"rnid {__version__}")
args = parser.parse_args() args = parser.parse_args()
@ -124,30 +124,30 @@ def main():
else: else:
identity_bytes = bytes.fromhex(args.import_str) identity_bytes = bytes.fromhex(args.import_str)
except Exception as e: except Exception as e:
print("Invalid identity data specified for import: "+str(e)) print(f"Invalid identity data specified for import: {e}")
exit(41) exit(41)
try: try:
identity = RNS.Identity.from_bytes(identity_bytes) identity = RNS.Identity.from_bytes(identity_bytes)
except Exception as e: except Exception as e:
print("Could not create Reticulum identity from specified data: "+str(e)) print(f"Could not create Reticulum identity from specified data: {e}")
exit(42) exit(42)
RNS.log("Identity imported") RNS.log("Identity imported")
if args.base64: if args.base64:
RNS.log("Public Key : "+base64.urlsafe_b64encode(identity.get_public_key()).decode("utf-8")) RNS.log(f"Public Key : {base64.urlsafe_b64encode(identity.get_public_key()).decode('utf-8')}")
elif args.base32: elif args.base32:
RNS.log("Public Key : "+base64.b32encode(identity.get_public_key()).decode("utf-8")) RNS.log(f"Public Key : {base64.b32encode(identity.get_public_key()).decode('utf-8')}")
else: else:
RNS.log("Public Key : "+RNS.hexrep(identity.get_public_key(), delimit=False)) RNS.log(f"Public Key : {RNS.hexrep(identity.get_public_key(), delimit=False)}")
if identity.prv: if identity.prv:
if args.print_private: if args.print_private:
if args.base64: if args.base64:
RNS.log("Private Key : "+base64.urlsafe_b64encode(identity.get_private_key()).decode("utf-8")) RNS.log(f"Private Key : {base64.urlsafe_b64encode(identity.get_private_key()).decode('utf-8')}")
elif args.base32: elif args.base32:
RNS.log("Private Key : "+base64.b32encode(identity.get_private_key()).decode("utf-8")) RNS.log(f"Private Key : {base64.b32encode(identity.get_private_key()).decode('utf-8')}")
else: else:
RNS.log("Private Key : "+RNS.hexrep(identity.get_private_key(), delimit=False)) RNS.log(f"Private Key : {RNS.hexrep(identity.get_private_key(), delimit=False)}")
else: else:
RNS.log("Private Key : Hidden") RNS.log("Private Key : Hidden")
@ -156,13 +156,13 @@ def main():
wp = os.path.expanduser(args.write) wp = os.path.expanduser(args.write)
if not os.path.isfile(wp) or args.force: if not os.path.isfile(wp) or args.force:
identity.to_file(wp) identity.to_file(wp)
RNS.log("Wrote imported identity to "+str(args.write)) RNS.log(f"Wrote imported identity to {args.write}")
else: else:
print("File "+str(wp)+" already exists, not overwriting") print(f"File {wp} already exists, not overwriting")
exit(43) exit(43)
except Exception as e: except Exception as e:
print("Error while writing imported identity to file: "+str(e)) print(f"Error while writing imported identity to file: {e}")
exit(44) exit(44)
exit(0) exit(0)
@ -189,16 +189,16 @@ def main():
if args.generate: if args.generate:
identity = RNS.Identity() identity = RNS.Identity()
if not args.force and os.path.isfile(args.generate): if not args.force and os.path.isfile(args.generate):
RNS.log("Identity file "+str(args.generate)+" already exists. Not overwriting.", RNS.LOG_ERROR) RNS.log(f"Identity file {args.generate} already exists. Not overwriting.", RNS.LOG_ERROR)
exit(3) exit(3)
else: else:
try: try:
identity.to_file(args.generate) identity.to_file(args.generate)
RNS.log("New identity written to "+str(args.generate)) RNS.log(f"New identity written to {args.generate}")
exit(0) exit(0)
except Exception as e: except Exception as e:
RNS.log("An error ocurred while saving the generated Identity.", RNS.LOG_ERROR) RNS.log("An error ocurred while saving the generated Identity.", RNS.LOG_ERROR)
RNS.log("The contained exception was: "+str(e), RNS.LOG_ERROR) RNS.log(f"The contained exception was: {e}", RNS.LOG_ERROR)
exit(4) exit(4)
identity = None identity = None
@ -210,24 +210,24 @@ def main():
if identity == None: if identity == None:
if not args.request: if not args.request:
RNS.log("Could not recall Identity for "+RNS.prettyhexrep(destination_hash)+".", RNS.LOG_ERROR) RNS.log(f"Could not recall Identity for {RNS.prettyhexrep(destination_hash)}.", RNS.LOG_ERROR)
RNS.log("You can query the network for unknown Identities with the -R option.", RNS.LOG_ERROR) RNS.log("You can query the network for unknown Identities with the -R option.", RNS.LOG_ERROR)
exit(5) exit(5)
else: else:
RNS.Transport.request_path(destination_hash) RNS.Transport.request_path(destination_hash)
def spincheck(): def spincheck():
return RNS.Identity.recall(destination_hash) != None return RNS.Identity.recall(destination_hash) != None
spin(spincheck, "Requesting unknown Identity for "+RNS.prettyhexrep(destination_hash), args.t) spin(spincheck, f"Requesting unknown Identity for {RNS.prettyhexrep(destination_hash)}", args.t)
if not spincheck(): if not spincheck():
RNS.log("Identity request timed out", RNS.LOG_ERROR) RNS.log("Identity request timed out", RNS.LOG_ERROR)
exit(6) exit(6)
else: else:
identity = RNS.Identity.recall(destination_hash) identity = RNS.Identity.recall(destination_hash)
RNS.log("Received Identity "+str(identity)+" for destination "+RNS.prettyhexrep(destination_hash)+" from the network") RNS.log(f"Received Identity {identity} for destination {RNS.prettyhexrep(destination_hash)} from the network")
else: else:
RNS.log("Recalled Identity "+str(identity)+" for destination "+RNS.prettyhexrep(destination_hash)) RNS.log(f"Recalled Identity {identity} for destination {RNS.prettyhexrep(destination_hash)}")
except Exception as e: except Exception as e:
@ -243,7 +243,7 @@ def main():
else: else:
try: try:
identity = RNS.Identity.from_file(identity_str) identity = RNS.Identity.from_file(identity_str)
RNS.log("Loaded Identity "+str(identity)+" from "+str(identity_str)) RNS.log(f"Loaded Identity {identity} from {identity_str}")
except Exception as e: except Exception as e:
RNS.log("Could not decode Identity from specified file") RNS.log("Could not decode Identity from specified file")
@ -261,15 +261,15 @@ def main():
aspects = aspects[1:] aspects = aspects[1:]
if identity.pub != None: if identity.pub != None:
destination = RNS.Destination(identity, RNS.Destination.OUT, RNS.Destination.SINGLE, app_name, *aspects) destination = RNS.Destination(identity, RNS.Destination.OUT, RNS.Destination.SINGLE, app_name, *aspects)
RNS.log("The "+str(args.hash)+" destination for this Identity is "+RNS.prettyhexrep(destination.hash)) RNS.log(f"The {args.hash} destination for this Identity is {RNS.prettyhexrep(destination.hash)}")
RNS.log("The full destination specifier is "+str(destination)) RNS.log(f"The full destination specifier is {destination}")
time.sleep(0.25) time.sleep(0.25)
exit(0) exit(0)
else: else:
raise KeyError("No public key known") raise KeyError("No public key known")
except Exception as e: except Exception as e:
RNS.log("An error ocurred while attempting to send the announce.", RNS.LOG_ERROR) RNS.log("An error ocurred while attempting to send the announce.", RNS.LOG_ERROR)
RNS.log("The contained exception was: "+str(e), RNS.LOG_ERROR) RNS.log(f"The contained exception was: {e}", RNS.LOG_ERROR)
exit(0) exit(0)
@ -284,39 +284,39 @@ def main():
aspects = aspects[1:] aspects = aspects[1:]
if identity.prv != None: if identity.prv != None:
destination = RNS.Destination(identity, RNS.Destination.IN, RNS.Destination.SINGLE, app_name, *aspects) destination = RNS.Destination(identity, RNS.Destination.IN, RNS.Destination.SINGLE, app_name, *aspects)
RNS.log("Created destination "+str(destination)) RNS.log(f"Created destination {destination}")
RNS.log("Announcing destination "+RNS.prettyhexrep(destination.hash)) RNS.log(f"Announcing destination {RNS.prettyhexrep(destination.hash)}")
destination.announce() destination.announce()
time.sleep(0.25) time.sleep(0.25)
exit(0) exit(0)
else: else:
destination = RNS.Destination(identity, RNS.Destination.OUT, RNS.Destination.SINGLE, app_name, *aspects) destination = RNS.Destination(identity, RNS.Destination.OUT, RNS.Destination.SINGLE, app_name, *aspects)
RNS.log("The "+str(args.announce)+" destination for this Identity is "+RNS.prettyhexrep(destination.hash)) RNS.log(f"The {args.announce} destination for this Identity is {RNS.prettyhexrep(destination.hash)}")
RNS.log("The full destination specifier is "+str(destination)) RNS.log(f"The full destination specifier is {destination}")
RNS.log("Cannot announce this destination, since the private key is not held") RNS.log("Cannot announce this destination, since the private key is not held")
time.sleep(0.25) time.sleep(0.25)
exit(33) exit(33)
except Exception as e: except Exception as e:
RNS.log("An error ocurred while attempting to send the announce.", RNS.LOG_ERROR) RNS.log("An error ocurred while attempting to send the announce.", RNS.LOG_ERROR)
RNS.log("The contained exception was: "+str(e), RNS.LOG_ERROR) RNS.log(f"The contained exception was: {e}", RNS.LOG_ERROR)
exit(0) exit(0)
if args.print_identity: if args.print_identity:
if args.base64: if args.base64:
RNS.log("Public Key : "+base64.urlsafe_b64encode(identity.get_public_key()).decode("utf-8")) RNS.log(f"Public Key : {base64.urlsafe_b64encode(identity.get_public_key()).decode('utf-8')}")
elif args.base32: elif args.base32:
RNS.log("Public Key : "+base64.b32encode(identity.get_public_key()).decode("utf-8")) RNS.log(f"Public Key : {base64.b32encode(identity.get_public_key()).decode('utf-8')}")
else: else:
RNS.log("Public Key : "+RNS.hexrep(identity.get_public_key(), delimit=False)) RNS.log(f"Public Key : {RNS.hexrep(identity.get_public_key(), delimit=False)}")
if identity.prv: if identity.prv:
if args.print_private: if args.print_private:
if args.base64: if args.base64:
RNS.log("Private Key : "+base64.urlsafe_b64encode(identity.get_private_key()).decode("utf-8")) RNS.log(f"Private Key : {base64.urlsafe_b64encode(identity.get_private_key()).decode('utf-8')}")
elif args.base32: elif args.base32:
RNS.log("Private Key : "+base64.b32encode(identity.get_private_key()).decode("utf-8")) RNS.log(f"Private Key : {base64.b32encode(identity.get_private_key()).decode('utf-8')}")
else: else:
RNS.log("Private Key : "+RNS.hexrep(identity.get_private_key(), delimit=False)) RNS.log(f"Private Key : {RNS.hexrep(identity.get_private_key(), delimit=False)}")
else: else:
RNS.log("Private Key : Hidden") RNS.log("Private Key : Hidden")
exit(0) exit(0)
@ -324,11 +324,11 @@ def main():
if args.export: if args.export:
if identity.prv: if identity.prv:
if args.base64: if args.base64:
RNS.log("Exported Identity : "+base64.urlsafe_b64encode(identity.get_private_key()).decode("utf-8")) RNS.log(f"Exported Identity : {base64.urlsafe_b64encode(identity.get_private_key()).decode('utf-8')}")
elif args.base32: elif args.base32:
RNS.log("Exported Identity : "+base64.b32encode(identity.get_private_key()).decode("utf-8")) RNS.log(f"Exported Identity : {base64.b32encode(identity.get_private_key()).decode('utf-8')}")
else: else:
RNS.log("Exported Identity : "+RNS.hexrep(identity.get_private_key(), delimit=False)) RNS.log(f"Exported Identity : {RNS.hexrep(identity.get_private_key(), delimit=False)}")
else: else:
RNS.log("Identity doesn't hold a private key, cannot export") RNS.log("Identity doesn't hold a private key, cannot export")
exit(50) exit(50)
@ -336,28 +336,28 @@ def main():
exit(0) exit(0)
if args.validate: if args.validate:
if not args.read and args.validate.lower().endswith("."+SIG_EXT): if not args.read and args.validate.lower().endswith(f".{SIG_EXT}"):
args.read = str(args.validate).replace("."+SIG_EXT, "") args.read = str(args.validate).replace(f".{SIG_EXT}", "")
if not os.path.isfile(args.validate): if not os.path.isfile(args.validate):
RNS.log("Signature file "+str(args.read)+" not found", RNS.LOG_ERROR) RNS.log(f"Signature file {args.read} not found", RNS.LOG_ERROR)
exit(10) exit(10)
if not os.path.isfile(args.read): if not os.path.isfile(args.read):
RNS.log("Input file "+str(args.read)+" not found", RNS.LOG_ERROR) RNS.log(f"Input file {args.read} not found", RNS.LOG_ERROR)
exit(11) exit(11)
data_input = None data_input = None
if args.read: if args.read:
if not os.path.isfile(args.read): if not os.path.isfile(args.read):
RNS.log("Input file "+str(args.read)+" not found", RNS.LOG_ERROR) RNS.log(f"Input file {args.read} not found", RNS.LOG_ERROR)
exit(12) exit(12)
else: else:
try: try:
data_input = open(args.read, "rb") data_input = open(args.read, "rb")
except Exception as e: except Exception as e:
RNS.log("Could not open input file for reading", RNS.LOG_ERROR) RNS.log("Could not open input file for reading", RNS.LOG_ERROR)
RNS.log("The contained exception was: "+str(e), RNS.LOG_ERROR) RNS.log(f"The contained exception was: {e}", RNS.LOG_ERROR)
exit(13) exit(13)
# TODO: Actually expand this to a good solution # TODO: Actually expand this to a good solution
@ -368,28 +368,28 @@ def main():
data_output = None data_output = None
if args.encrypt and not args.write and not args.stdout and args.read: if args.encrypt and not args.write and not args.stdout and args.read:
args.write = str(args.read)+"."+ENCRYPT_EXT args.write = f"{args.read}.{ENCRYPT_EXT}"
if args.decrypt and not args.write and not args.stdout and args.read and args.read.lower().endswith("."+ENCRYPT_EXT): if args.decrypt and not args.write and not args.stdout and args.read and args.read.lower().endswith(f".{ENCRYPT_EXT}"):
args.write = str(args.read).replace("."+ENCRYPT_EXT, "") args.write = str(args.read).replace(f".{ENCRYPT_EXT}", "")
if args.sign and identity.prv == None: if args.sign and identity.prv == None:
RNS.log("Specified Identity does not hold a private key. Cannot sign.", RNS.LOG_ERROR) RNS.log("Specified Identity does not hold a private key. Cannot sign.", RNS.LOG_ERROR)
exit(14) exit(14)
if args.sign and not args.write and not args.stdout and args.read: if args.sign and not args.write and not args.stdout and args.read:
args.write = str(args.read)+"."+SIG_EXT args.write = f"{args.read}.{SIG_EXT}"
if args.write: if args.write:
if not args.force and os.path.isfile(args.write): if not args.force and os.path.isfile(args.write):
RNS.log("Output file "+str(args.write)+" already exists. Not overwriting.", RNS.LOG_ERROR) RNS.log(f"Output file {args.write} already exists. Not overwriting.", RNS.LOG_ERROR)
exit(15) exit(15)
else: else:
try: try:
data_output = open(args.write, "wb") data_output = open(args.write, "wb")
except Exception as e: except Exception as e:
RNS.log("Could not open output file for writing", RNS.LOG_ERROR) RNS.log("Could not open output file for writing", RNS.LOG_ERROR)
RNS.log("The contained exception was: "+str(e), RNS.LOG_ERROR) RNS.log(f"The contained exception was: {e}", RNS.LOG_ERROR)
exit(15) exit(15)
# TODO: Actually expand this to a good solution # TODO: Actually expand this to a good solution
@ -414,7 +414,7 @@ def main():
exit(18) exit(18)
if not args.stdout: if not args.stdout:
RNS.log("Signing "+str(args.read)) RNS.log(f"Signing {args.read}")
try: try:
data_output.write(identity.sign(data_input.read())) data_output.write(identity.sign(data_input.read()))
@ -423,13 +423,13 @@ def main():
if not args.stdout: if not args.stdout:
if args.read: if args.read:
RNS.log("File "+str(args.read)+" signed with "+str(identity)+" to "+str(args.write)) RNS.log(f"File {args.read} signed with {identity} to {args.write}")
exit(0) exit(0)
except Exception as e: except Exception as e:
if not args.stdout: if not args.stdout:
RNS.log("An error ocurred while encrypting data.", RNS.LOG_ERROR) RNS.log("An error ocurred while encrypting data.", RNS.LOG_ERROR)
RNS.log("The contained exception was: "+str(e), RNS.LOG_ERROR) RNS.log(f"The contained exception was: {e}", RNS.LOG_ERROR)
try: try:
data_output.close() data_output.close()
except: except:
@ -453,8 +453,8 @@ def main():
try: try:
sig_input = open(args.validate, "rb") sig_input = open(args.validate, "rb")
except Exception as e: except Exception as e:
RNS.log("An error ocurred while opening "+str(args.validate)+".", RNS.LOG_ERROR) RNS.log(f"An error ocurred while opening {args.validate}.", RNS.LOG_ERROR)
RNS.log("The contained exception was: "+str(e), RNS.LOG_ERROR) RNS.log(f"The contained exception was: {e}", RNS.LOG_ERROR)
exit(21) exit(21)
@ -464,17 +464,17 @@ def main():
if not validated: if not validated:
if not args.stdout: if not args.stdout:
RNS.log("Signature "+str(args.validate)+" for file "+str(args.read)+" is invalid", RNS.LOG_ERROR) RNS.log(f"Signature {args.validate} for file {args.read} is invalid", RNS.LOG_ERROR)
exit(22) exit(22)
else: else:
if not args.stdout: if not args.stdout:
RNS.log("Signature "+str(args.validate)+" for file "+str(args.read)+" made by Identity "+str(identity)+" is valid") RNS.log(f"Signature {args.validate} for file {args.read} made by Identity {identity} is valid")
exit(0) exit(0)
except Exception as e: except Exception as e:
if not args.stdout: if not args.stdout:
RNS.log("An error ocurred while validating signature.", RNS.LOG_ERROR) RNS.log("An error ocurred while validating signature.", RNS.LOG_ERROR)
RNS.log("The contained exception was: "+str(e), RNS.LOG_ERROR) RNS.log(f"The contained exception was: {e}", RNS.LOG_ERROR)
try: try:
data_output.close() data_output.close()
except: except:
@ -497,7 +497,7 @@ def main():
exit(25) exit(25)
if not args.stdout: if not args.stdout:
RNS.log("Encrypting "+str(args.read)) RNS.log(f"Encrypting {args.read}")
try: try:
more_data = True more_data = True
@ -511,13 +511,13 @@ def main():
data_input.close() data_input.close()
if not args.stdout: if not args.stdout:
if args.read: if args.read:
RNS.log("File "+str(args.read)+" encrypted for "+str(identity)+" to "+str(args.write)) RNS.log(f"File {args.read} encrypted for {identity} to {args.write}")
exit(0) exit(0)
except Exception as e: except Exception as e:
if not args.stdout: if not args.stdout:
RNS.log("An error ocurred while encrypting data.", RNS.LOG_ERROR) RNS.log("An error ocurred while encrypting data.", RNS.LOG_ERROR)
RNS.log("The contained exception was: "+str(e), RNS.LOG_ERROR) RNS.log(f"The contained exception was: {e}", RNS.LOG_ERROR)
try: try:
data_output.close() data_output.close()
except: except:
@ -544,7 +544,7 @@ def main():
exit(29) exit(29)
if not args.stdout: if not args.stdout:
RNS.log("Decrypting "+str(args.read)+"...") RNS.log(f"Decrypting {args.read}...")
try: try:
more_data = True more_data = True
@ -564,13 +564,13 @@ def main():
data_input.close() data_input.close()
if not args.stdout: if not args.stdout:
if args.read: if args.read:
RNS.log("File "+str(args.read)+" decrypted with "+str(identity)+" to "+str(args.write)) RNS.log(f"File {args.read} decrypted with {identity} to {args.write}")
exit(0) exit(0)
except Exception as e: except Exception as e:
if not args.stdout: if not args.stdout:
RNS.log("An error ocurred while decrypting data.", RNS.LOG_ERROR) RNS.log("An error ocurred while decrypting data.", RNS.LOG_ERROR)
RNS.log("The contained exception was: "+str(e), RNS.LOG_ERROR) RNS.log(f"The contained exception was: {e}", RNS.LOG_ERROR)
try: try:
data_output.close() data_output.close()
except: except:

View File

@ -48,7 +48,7 @@ def main():
parser.add_argument('-v', '--verbose', action='count', default=0) parser.add_argument('-v', '--verbose', action='count', default=0)
parser.add_argument('-q', '--quiet', action='count', default=0) parser.add_argument('-q', '--quiet', action='count', default=0)
parser.add_argument("--exampleconfig", action='store_true', default=False, help="print verbose configuration example to stdout and exit") parser.add_argument("--exampleconfig", action='store_true', default=False, help="print verbose configuration example to stdout and exit")
parser.add_argument("--version", action="version", version="ir {version}".format(version=__version__)) parser.add_argument("--version", action="version", version=f"ir {__version__}")
args = parser.parse_args() args = parser.parse_args()

File diff suppressed because one or more lines are too long

View File

@ -35,7 +35,7 @@ def connect_remote(destination_hash, auth_identity, timeout, no_output = False):
global remote_link, reticulum global remote_link, reticulum
if not RNS.Transport.has_path(destination_hash): if not RNS.Transport.has_path(destination_hash):
if not no_output: if not no_output:
print("Path to "+RNS.prettyhexrep(destination_hash)+" requested", end=" ") print(f"Path to {RNS.prettyhexrep(destination_hash)} requested", end=" ")
sys.stdout.flush() sys.stdout.flush()
RNS.Transport.request_path(destination_hash) RNS.Transport.request_path(destination_hash)
pr_time = time.time() pr_time = time.time()
@ -88,7 +88,7 @@ def program_setup(configdir, table, rates, drop, destination_hexhash, verbosity,
try: try:
dest_len = (RNS.Reticulum.TRUNCATED_HASHLENGTH//8)*2 dest_len = (RNS.Reticulum.TRUNCATED_HASHLENGTH//8)*2
if len(remote) != dest_len: if len(remote) != dest_len:
raise ValueError("Destination length is invalid, must be {hex} hexadecimal characters ({byte} bytes).".format(hex=dest_len, byte=dest_len//2)) raise ValueError(f"Destination length is invalid, must be {dest_len} hexadecimal characters ({dest_len // 2} bytes).")
try: try:
identity_hash = bytes.fromhex(remote) identity_hash = bytes.fromhex(remote)
remote_hash = RNS.Destination.hash_from_name_and_identity("rnstransport.remote.management", identity_hash) remote_hash = RNS.Destination.hash_from_name_and_identity("rnstransport.remote.management", identity_hash)
@ -97,7 +97,7 @@ def program_setup(configdir, table, rates, drop, destination_hexhash, verbosity,
identity = RNS.Identity.from_file(os.path.expanduser(management_identity)) identity = RNS.Identity.from_file(os.path.expanduser(management_identity))
if identity == None: if identity == None:
raise ValueError("Could not load management identity from "+str(management_identity)) raise ValueError(f"Could not load management identity from {management_identity}")
try: try:
connect_remote(remote_hash, identity, remote_timeout, no_output) connect_remote(remote_hash, identity, remote_timeout, no_output)
@ -118,7 +118,7 @@ def program_setup(configdir, table, rates, drop, destination_hexhash, verbosity,
try: try:
dest_len = (RNS.Reticulum.TRUNCATED_HASHLENGTH//8)*2 dest_len = (RNS.Reticulum.TRUNCATED_HASHLENGTH//8)*2
if len(destination_hexhash) != dest_len: if len(destination_hexhash) != dest_len:
raise ValueError("Destination length is invalid, must be {hex} hexadecimal characters ({byte} bytes).".format(hex=dest_len, byte=dest_len//2)) raise ValueError(f"Destination length is invalid, must be {dest_len} hexadecimal characters ({dest_len // 2} bytes).")
try: try:
destination_hash = bytes.fromhex(destination_hexhash) destination_hash = bytes.fromhex(destination_hexhash)
except Exception as e: except Exception as e:
@ -166,7 +166,7 @@ def program_setup(configdir, table, rates, drop, destination_hexhash, verbosity,
m_str = " " m_str = " "
else: else:
m_str = "s" m_str = "s"
print(RNS.prettyhexrep(path["hash"])+" is "+str(path["hops"])+" hop"+m_str+" away via "+RNS.prettyhexrep(path["via"])+" on "+path["interface"]+" expires "+RNS.timestamp_str(path["expires"])) print(f"{RNS.prettyhexrep(path['hash'])} is {path['hops']} hop{m_str} away via {RNS.prettyhexrep(path['via'])} on {path['interface']} expires {RNS.timestamp_str(path['expires'])}")
if destination_hash != None and displayed == 0: if destination_hash != None and displayed == 0:
print("No path known") print("No path known")
@ -178,7 +178,7 @@ def program_setup(configdir, table, rates, drop, destination_hexhash, verbosity,
try: try:
dest_len = (RNS.Reticulum.TRUNCATED_HASHLENGTH//8)*2 dest_len = (RNS.Reticulum.TRUNCATED_HASHLENGTH//8)*2
if len(destination_hexhash) != dest_len: if len(destination_hexhash) != dest_len:
raise ValueError("Destination length is invalid, must be {hex} hexadecimal characters ({byte} bytes).".format(hex=dest_len, byte=dest_len//2)) raise ValueError(f"Destination length is invalid, must be {dest_len} hexadecimal characters ({dest_len // 2} bytes).")
try: try:
destination_hash = bytes.fromhex(destination_hexhash) destination_hash = bytes.fromhex(destination_hexhash)
except Exception as e: except Exception as e:
@ -242,21 +242,21 @@ def program_setup(configdir, table, rates, drop, destination_hexhash, verbosity,
else: else:
s_str = "s" s_str = "s"
rv_str = ", "+str(entry["rate_violations"])+" active rate violation"+s_str rv_str = f", {entry['rate_violations']} active rate violation{s_str}"
else: else:
rv_str = "" rv_str = ""
if entry["blocked_until"] > time.time(): if entry["blocked_until"] > time.time():
bli = time.time()-(int(entry["blocked_until"])-time.time()) bli = time.time()-(int(entry["blocked_until"])-time.time())
bl_str = ", new announces allowed in "+pretty_date(int(bli)) bl_str = f", new announces allowed in {pretty_date(int(bli))}"
else: else:
bl_str = "" bl_str = ""
print(RNS.prettyhexrep(entry["hash"])+" last heard "+last_str+" ago, "+str(hour_rate)+" announces/hour in the last "+span_str+rv_str+bl_str) print(f"{RNS.prettyhexrep(entry['hash'])} last heard {last_str} ago, {hour_rate} announces/hour in the last {span_str}{rv_str}{bl_str}")
except Exception as e: except Exception as e:
print("Error while processing entry for "+RNS.prettyhexrep(entry["hash"])) print(f"Error while processing entry for {RNS.prettyhexrep(entry['hash'])}")
print(str(e)) print(str(e))
if destination_hash != None and displayed == 0: if destination_hash != None and displayed == 0:
@ -283,7 +283,7 @@ def program_setup(configdir, table, rates, drop, destination_hexhash, verbosity,
try: try:
dest_len = (RNS.Reticulum.TRUNCATED_HASHLENGTH//8)*2 dest_len = (RNS.Reticulum.TRUNCATED_HASHLENGTH//8)*2
if len(destination_hexhash) != dest_len: if len(destination_hexhash) != dest_len:
raise ValueError("Destination length is invalid, must be {hex} hexadecimal characters ({byte} bytes).".format(hex=dest_len, byte=dest_len//2)) raise ValueError(f"Destination length is invalid, must be {dest_len} hexadecimal characters ({dest_len // 2} bytes).")
try: try:
destination_hash = bytes.fromhex(destination_hexhash) destination_hash = bytes.fromhex(destination_hexhash)
except Exception as e: except Exception as e:
@ -293,9 +293,9 @@ def program_setup(configdir, table, rates, drop, destination_hexhash, verbosity,
sys.exit(1) sys.exit(1)
if reticulum.drop_path(destination_hash): if reticulum.drop_path(destination_hash):
print("Dropped path to "+RNS.prettyhexrep(destination_hash)) print(f"Dropped path to {RNS.prettyhexrep(destination_hash)}")
else: else:
print("Unable to drop path to "+RNS.prettyhexrep(destination_hash)+". Does it exist?") print(f"Unable to drop path to {RNS.prettyhexrep(destination_hash)}. Does it exist?")
sys.exit(1) sys.exit(1)
elif drop_via: elif drop_via:
@ -308,7 +308,7 @@ def program_setup(configdir, table, rates, drop, destination_hexhash, verbosity,
try: try:
dest_len = (RNS.Reticulum.TRUNCATED_HASHLENGTH//8)*2 dest_len = (RNS.Reticulum.TRUNCATED_HASHLENGTH//8)*2
if len(destination_hexhash) != dest_len: if len(destination_hexhash) != dest_len:
raise ValueError("Destination length is invalid, must be {hex} hexadecimal characters ({byte} bytes).".format(hex=dest_len, byte=dest_len//2)) raise ValueError(f"Destination length is invalid, must be {dest_len} hexadecimal characters ({dest_len // 2} bytes).")
try: try:
destination_hash = bytes.fromhex(destination_hexhash) destination_hash = bytes.fromhex(destination_hexhash)
except Exception as e: except Exception as e:
@ -318,9 +318,9 @@ def program_setup(configdir, table, rates, drop, destination_hexhash, verbosity,
sys.exit(1) sys.exit(1)
if reticulum.drop_all_via(destination_hash): if reticulum.drop_all_via(destination_hash):
print("Dropped all paths via "+RNS.prettyhexrep(destination_hash)) print(f"Dropped all paths via {RNS.prettyhexrep(destination_hash)}")
else: else:
print("Unable to drop paths via "+RNS.prettyhexrep(destination_hash)+". Does the transport instance exist?") print(f"Unable to drop paths via {RNS.prettyhexrep(destination_hash)}. Does the transport instance exist?")
sys.exit(1) sys.exit(1)
else: else:
@ -333,7 +333,7 @@ def program_setup(configdir, table, rates, drop, destination_hexhash, verbosity,
try: try:
dest_len = (RNS.Reticulum.TRUNCATED_HASHLENGTH//8)*2 dest_len = (RNS.Reticulum.TRUNCATED_HASHLENGTH//8)*2
if len(destination_hexhash) != dest_len: if len(destination_hexhash) != dest_len:
raise ValueError("Destination length is invalid, must be {hex} hexadecimal characters ({byte} bytes).".format(hex=dest_len, byte=dest_len//2)) raise ValueError(f"Destination length is invalid, must be {dest_len} hexadecimal characters ({dest_len // 2} bytes).")
try: try:
destination_hash = bytes.fromhex(destination_hexhash) destination_hash = bytes.fromhex(destination_hexhash)
except Exception as e: except Exception as e:
@ -344,7 +344,7 @@ def program_setup(configdir, table, rates, drop, destination_hexhash, verbosity,
if not RNS.Transport.has_path(destination_hash): if not RNS.Transport.has_path(destination_hash):
RNS.Transport.request_path(destination_hash) RNS.Transport.request_path(destination_hash)
print("Path to "+RNS.prettyhexrep(destination_hash)+" requested ", end=" ") print(f"Path to {RNS.prettyhexrep(destination_hash)} requested ", end=" ")
sys.stdout.flush() sys.stdout.flush()
i = 0 i = 0
@ -352,7 +352,7 @@ def program_setup(configdir, table, rates, drop, destination_hexhash, verbosity,
limit = time.time()+timeout limit = time.time()+timeout
while not RNS.Transport.has_path(destination_hash) and time.time()<limit: while not RNS.Transport.has_path(destination_hash) and time.time()<limit:
time.sleep(0.1) time.sleep(0.1)
print(("\b\b"+syms[i]+" "), end="") print(f"\b\b{syms[i]} ", end="")
sys.stdout.flush() sys.stdout.flush()
i = (i+1)%len(syms) i = (i+1)%len(syms)
@ -371,7 +371,7 @@ def program_setup(configdir, table, rates, drop, destination_hexhash, verbosity,
else: else:
ms = "" ms = ""
print("\rPath found, destination "+RNS.prettyhexrep(destination_hash)+" is "+str(hops)+" hop"+ms+" away via "+next_hop+" on "+next_hop_interface) print(f'\rPath found, destination {RNS.prettyhexrep(destination_hash)} is {hops} hop{ms} away via {next_hop} on {next_hop_interface}')
else: else:
print("\r \rPath not found") print("\r \rPath not found")
sys.exit(1) sys.exit(1)
@ -392,7 +392,7 @@ def main():
parser.add_argument( parser.add_argument(
"--version", "--version",
action="version", action="version",
version="rnpath {version}".format(version=__version__) version=f"rnpath {__version__}"
) )
parser.add_argument( parser.add_argument(
@ -547,26 +547,26 @@ def pretty_date(time=False):
return '' return ''
if day_diff == 0: if day_diff == 0:
if second_diff < 10: if second_diff < 10:
return str(second_diff) + " seconds" return f"{second_diff} seconds"
if second_diff < 60: if second_diff < 60:
return str(second_diff) + " seconds" return f"{second_diff} seconds"
if second_diff < 120: if second_diff < 120:
return "1 minute" return "1 minute"
if second_diff < 3600: if second_diff < 3600:
return str(int(second_diff / 60)) + " minutes" return f"{int(second_diff / 60)} minutes"
if second_diff < 7200: if second_diff < 7200:
return "an hour" return "an hour"
if second_diff < 86400: if second_diff < 86400:
return str(int(second_diff / 3600)) + " hours" return f"{int(second_diff / 3600)} hours"
if day_diff == 1: if day_diff == 1:
return "1 day" return "1 day"
if day_diff < 7: if day_diff < 7:
return str(day_diff) + " days" return f"{day_diff} days"
if day_diff < 31: if day_diff < 31:
return str(int(day_diff / 7)) + " weeks" return f"{int(day_diff / 7)} weeks"
if day_diff < 365: if day_diff < 365:
return str(int(day_diff / 30)) + " months" return f"{int(day_diff / 30)} months"
return str(int(day_diff / 365)) + " years" return f"{int(day_diff / 365)} years"
if __name__ == "__main__": if __name__ == "__main__":
main() main()

View File

@ -49,7 +49,7 @@ def program_setup(configdir, destination_hexhash, size=None, full_name = None, v
try: try:
dest_len = (RNS.Reticulum.TRUNCATED_HASHLENGTH//8)*2 dest_len = (RNS.Reticulum.TRUNCATED_HASHLENGTH//8)*2
if len(destination_hexhash) != dest_len: if len(destination_hexhash) != dest_len:
raise ValueError("Destination length is invalid, must be {hex} hexadecimal characters ({byte} bytes).".format(hex=dest_len, byte=dest_len//2)) raise ValueError(f"Destination length is invalid, must be {dest_len} hexadecimal characters ({dest_len // 2} bytes).")
try: try:
destination_hash = bytes.fromhex(destination_hexhash) destination_hash = bytes.fromhex(destination_hexhash)
except Exception as e: except Exception as e:
@ -70,7 +70,7 @@ def program_setup(configdir, destination_hexhash, size=None, full_name = None, v
if not RNS.Transport.has_path(destination_hash): if not RNS.Transport.has_path(destination_hash):
RNS.Transport.request_path(destination_hash) RNS.Transport.request_path(destination_hash)
print("Path to "+RNS.prettyhexrep(destination_hash)+" requested ", end=" ") print(f"Path to {RNS.prettyhexrep(destination_hash)} requested ", end=" ")
sys.stdout.flush() sys.stdout.flush()
_timeout = time.time() + (timeout or DEFAULT_TIMEOUT+reticulum.get_first_hop_timeout(destination_hash)) _timeout = time.time() + (timeout or DEFAULT_TIMEOUT+reticulum.get_first_hop_timeout(destination_hash))
@ -78,7 +78,7 @@ def program_setup(configdir, destination_hexhash, size=None, full_name = None, v
syms = "⢄⢂⢁⡁⡈⡐⡠" syms = "⢄⢂⢁⡁⡈⡐⡠"
while not RNS.Transport.has_path(destination_hash) and not time.time() > _timeout: while not RNS.Transport.has_path(destination_hash) and not time.time() > _timeout:
time.sleep(0.1) time.sleep(0.1)
print(("\b\b"+syms[i]+" "), end="") print(f"\b\b{syms[i]} ", end="")
sys.stdout.flush() sys.stdout.flush()
i = (i+1)%len(syms) i = (i+1)%len(syms)
@ -107,7 +107,7 @@ def program_setup(configdir, destination_hexhash, size=None, full_name = None, v
probe = RNS.Packet(request_destination, os.urandom(size)) probe = RNS.Packet(request_destination, os.urandom(size))
probe.pack() probe.pack()
except OSError: except OSError:
print("Error: Probe packet size of "+str(len(probe.raw))+" bytes exceed MTU of "+str(RNS.Reticulum.MTU)+" bytes") print(f"Error: Probe packet size of {len(probe.raw)} bytes exceed MTU of {RNS.Reticulum.MTU} bytes")
exit(3) exit(3)
receipt = probe.send() receipt = probe.send()
@ -115,19 +115,19 @@ def program_setup(configdir, destination_hexhash, size=None, full_name = None, v
if more_output: if more_output:
nhd = reticulum.get_next_hop(destination_hash) nhd = reticulum.get_next_hop(destination_hash)
via_str = " via "+RNS.prettyhexrep(nhd) if nhd != None else "" via_str = f" via {RNS.prettyhexrep(nhd)}" if nhd != None else ""
if_str = " on "+str(reticulum.get_next_hop_if_name(destination_hash)) if reticulum.get_next_hop_if_name(destination_hash) != "None" else "" if_str = f" on {reticulum.get_next_hop_if_name(destination_hash)}" if reticulum.get_next_hop_if_name(destination_hash) != "None" else ""
more = via_str+if_str more = via_str+if_str
else: else:
more = "" more = ""
print("\rSent probe "+str(sent)+" ("+str(size)+" bytes) to "+RNS.prettyhexrep(destination_hash)+more+" ", end=" ") print(f'\rSent probe {sent} ({size} bytes) to {RNS.prettyhexrep(destination_hash)}{more} ', end=" ")
_timeout = time.time() + (timeout or DEFAULT_TIMEOUT+reticulum.get_first_hop_timeout(destination_hash)) _timeout = time.time() + (timeout or DEFAULT_TIMEOUT+reticulum.get_first_hop_timeout(destination_hash))
i = 0 i = 0
while receipt.status == RNS.PacketReceipt.SENT and not time.time() > _timeout: while receipt.status == RNS.PacketReceipt.SENT and not time.time() > _timeout:
time.sleep(0.1) time.sleep(0.1)
print(("\b\b"+syms[i]+" "), end="") print(f"\b\b{syms[i]} ", end="")
sys.stdout.flush() sys.stdout.flush()
i = (i+1)%len(syms) i = (i+1)%len(syms)
@ -149,10 +149,10 @@ def program_setup(configdir, destination_hexhash, size=None, full_name = None, v
rtt = receipt.get_rtt() rtt = receipt.get_rtt()
if (rtt >= 1): if (rtt >= 1):
rtt = round(rtt, 3) rtt = round(rtt, 3)
rttstring = str(rtt)+" seconds" rttstring = f"{rtt} seconds"
else: else:
rtt = round(rtt*1000, 3) rtt = round(rtt*1000, 3)
rttstring = str(rtt)+" milliseconds" rttstring = f"{rtt} milliseconds"
reception_stats = "" reception_stats = ""
if reticulum.is_connected_to_shared_instance: if reticulum.is_connected_to_shared_instance:
@ -161,28 +161,24 @@ def program_setup(configdir, destination_hexhash, size=None, full_name = None, v
reception_q = reticulum.get_packet_q(receipt.proof_packet.packet_hash) reception_q = reticulum.get_packet_q(receipt.proof_packet.packet_hash)
if reception_rssi != None: if reception_rssi != None:
reception_stats += " [RSSI "+str(reception_rssi)+" dBm]" reception_stats += f" [RSSI {reception_rssi} dBm]"
if reception_snr != None: if reception_snr != None:
reception_stats += " [SNR "+str(reception_snr)+" dB]" reception_stats += f" [SNR {reception_snr} dB]"
if reception_q != None: if reception_q != None:
reception_stats += " [Link Quality "+str(reception_q)+"%]" reception_stats += f" [Link Quality {reception_q}%]"
else: else:
if receipt.proof_packet != None: if receipt.proof_packet != None:
if receipt.proof_packet.rssi != None: if receipt.proof_packet.rssi != None:
reception_stats += " [RSSI "+str(receipt.proof_packet.rssi)+" dBm]" reception_stats += f" [RSSI {receipt.proof_packet.rssi} dBm]"
if receipt.proof_packet.snr != None: if receipt.proof_packet.snr != None:
reception_stats += " [SNR "+str(receipt.proof_packet.snr)+" dB]" reception_stats += f" [SNR {receipt.proof_packet.snr} dB]"
print( print(
"Valid reply from "+ f"Valid reply from {RNS.prettyhexrep(receipt.destination.hash)}\nRound-trip time is {rttstring} over {hops} hop{ms}{reception_stats}\n"
RNS.prettyhexrep(receipt.destination.hash)+
"\nRound-trip time is "+rttstring+
" over "+str(hops)+" hop"+ms+
reception_stats+"\n"
) )
else: else:
@ -207,7 +203,7 @@ def main():
parser.add_argument("-n", "--probes", action="store", default=1, help="number of probes to send", type=int) parser.add_argument("-n", "--probes", action="store", default=1, help="number of probes to send", type=int)
parser.add_argument("-t", "--timeout", metavar="seconds", action="store", default=None, help="timeout before giving up", type=float) parser.add_argument("-t", "--timeout", metavar="seconds", action="store", default=None, help="timeout before giving up", type=float)
parser.add_argument("-w", "--wait", metavar="seconds", action="store", default=0, help="time between each probe", type=float) parser.add_argument("-w", "--wait", metavar="seconds", action="store", default=0, help="time between each probe", type=float)
parser.add_argument("--version", action="version", version="rnprobe {version}".format(version=__version__)) parser.add_argument("--version", action="version", version=f"rnprobe {__version__}")
parser.add_argument("full_name", nargs="?", default=None, help="full destination name in dotted notation", type=str) parser.add_argument("full_name", nargs="?", default=None, help="full destination name in dotted notation", type=str)
parser.add_argument("destination_hash", nargs="?", default=None, help="hexadecimal hash of the destination", type=str) parser.add_argument("destination_hash", nargs="?", default=None, help="hexadecimal hash of the destination", type=str)

View File

@ -40,9 +40,9 @@ def program_setup(configdir, verbosity = 0, quietness = 0, service = False):
reticulum = RNS.Reticulum(configdir=configdir, verbosity=targetverbosity, logdest=targetlogdest) reticulum = RNS.Reticulum(configdir=configdir, verbosity=targetverbosity, logdest=targetlogdest)
if reticulum.is_connected_to_shared_instance: if reticulum.is_connected_to_shared_instance:
RNS.log("Started rnsd version {version} connected to another shared local instance, this is probably NOT what you want!".format(version=__version__), RNS.LOG_WARNING) RNS.log(f"Started rnsd version {__version__} connected to another shared local instance, this is probably NOT what you want!", RNS.LOG_WARNING)
else: else:
RNS.log("Started rnsd version {version}".format(version=__version__), RNS.LOG_NOTICE) RNS.log(f"Started rnsd version {__version__}", RNS.LOG_NOTICE)
while True: while True:
time.sleep(1) time.sleep(1)
@ -55,7 +55,7 @@ def main():
parser.add_argument('-q', '--quiet', action='count', default=0) parser.add_argument('-q', '--quiet', action='count', default=0)
parser.add_argument('-s', '--service', action='store_true', default=False, help="rnsd is running as a service and should log to file") parser.add_argument('-s', '--service', action='store_true', default=False, help="rnsd is running as a service and should log to file")
parser.add_argument("--exampleconfig", action='store_true', default=False, help="print verbose configuration example to stdout and exit") parser.add_argument("--exampleconfig", action='store_true', default=False, help="print verbose configuration example to stdout and exit")
parser.add_argument("--version", action="version", version="rnsd {version}".format(version=__version__)) parser.add_argument("--version", action="version", version=f"rnsd {__version__}")
args = parser.parse_args() args = parser.parse_args()

View File

@ -42,12 +42,12 @@ def size_str(num, suffix='B'):
for unit in units: for unit in units:
if abs(num) < 1000.0: if abs(num) < 1000.0:
if unit == "": if unit == "":
return "%.0f %s%s" % (num, unit, suffix) return f"{num:.0f} {unit}{suffix}"
else: else:
return "%.2f %s%s" % (num, unit, suffix) return f"{num:.2f} {unit}{suffix}"
num /= 1000.0 num /= 1000.0
return "%.2f%s%s" % (num, last_unit, suffix) return f"{num:.2f}{last_unit}{suffix}"
request_result = None request_result = None
request_concluded = False request_concluded = False
@ -144,7 +144,7 @@ def program_setup(configdir, dispall=False, verbosity=0, name_filter=None, json=
try: try:
dest_len = (RNS.Reticulum.TRUNCATED_HASHLENGTH//8)*2 dest_len = (RNS.Reticulum.TRUNCATED_HASHLENGTH//8)*2
if len(remote) != dest_len: if len(remote) != dest_len:
raise ValueError("Destination length is invalid, must be {hex} hexadecimal characters ({byte} bytes).".format(hex=dest_len, byte=dest_len//2)) raise ValueError(f"Destination length is invalid, must be {dest_len} hexadecimal characters ({dest_len // 2} bytes).")
try: try:
identity_hash = bytes.fromhex(remote) identity_hash = bytes.fromhex(remote)
destination_hash = RNS.Destination.hash_from_name_and_identity("rnstransport.remote.management", identity_hash) destination_hash = RNS.Destination.hash_from_name_and_identity("rnstransport.remote.management", identity_hash)
@ -281,13 +281,13 @@ def program_setup(configdir, dispall=False, verbosity=0, name_filter=None, json=
if "ifac_netname" in ifstat and ifstat["ifac_netname"] != None: if "ifac_netname" in ifstat and ifstat["ifac_netname"] != None:
print(" Network : {nn}".format(nn=ifstat["ifac_netname"])) print(" Network : {nn}".format(nn=ifstat["ifac_netname"]))
print(" Status : {ss}".format(ss=ss)) print(f" Status : {ss}")
if clients != None and clients_string != "": if clients != None and clients_string != "":
print(" "+clients_string) print(" "+clients_string)
if not (name.startswith("Shared Instance[") or name.startswith("TCPInterface[Client") or name.startswith("LocalInterface[")): if not (name.startswith("Shared Instance[") or name.startswith("TCPInterface[Client") or name.startswith("LocalInterface[")):
print(" Mode : {mode}".format(mode=modestr)) print(f" Mode : {modestr}")
if "bitrate" in ifstat and ifstat["bitrate"] != None: if "bitrate" in ifstat and ifstat["bitrate"] != None:
print(" Rate : {ss}".format(ss=speed_str(ifstat["bitrate"]))) print(" Rate : {ss}".format(ss=speed_str(ifstat["bitrate"])))
@ -295,7 +295,8 @@ def program_setup(configdir, dispall=False, verbosity=0, name_filter=None, json=
if "battery_percent" in ifstat and ifstat["battery_percent"] != None: if "battery_percent" in ifstat and ifstat["battery_percent"] != None:
try: try:
bpi = int(ifstat["battery_percent"]) bpi = int(ifstat["battery_percent"])
print(" Battery : {bp}%".format(bp=bpi)) bss = ifstat["battery_state"]
print(f" Battery : {bpi}% ({bss})")
except: except:
pass pass
@ -488,10 +489,10 @@ def speed_str(num, suffix='bps'):
for unit in units: for unit in units:
if abs(num) < 1000.0: if abs(num) < 1000.0:
return "%3.2f %s%s" % (num, unit, suffix) return f"{num:3.2f} {unit}{suffix}"
num /= 1000.0 num /= 1000.0
return "%.2f %s%s" % (num, last_unit, suffix) return f"{num:.2f} {last_unit}{suffix}"
if __name__ == "__main__": if __name__ == "__main__":
main() main()

View File

@ -28,8 +28,8 @@ import argparse
import shlex import shlex
import time import time
import sys import sys
import tty
import os import os
#import tty
from RNS._version import __version__ from RNS._version import __version__
@ -42,7 +42,7 @@ allowed_identity_hashes = []
def prepare_identity(identity_path): def prepare_identity(identity_path):
global identity global identity
if identity_path == None: if identity_path == None:
identity_path = RNS.Reticulum.identitypath+"/"+APP_NAME identity_path = f"{RNS.Reticulum.identitypath}/{APP_NAME}"
if os.path.isfile(identity_path): if os.path.isfile(identity_path):
identity = RNS.Identity.from_file(identity_path) identity = RNS.Identity.from_file(identity_path)
@ -62,8 +62,8 @@ def listen(configdir, identitypath = None, verbosity = 0, quietness = 0, allowed
destination = RNS.Destination(identity, RNS.Destination.IN, RNS.Destination.SINGLE, APP_NAME, "execute") destination = RNS.Destination(identity, RNS.Destination.IN, RNS.Destination.SINGLE, APP_NAME, "execute")
if print_identity: if print_identity:
print("Identity : "+str(identity)) print(f"Identity : {identity}")
print("Listening on : "+RNS.prettyhexrep(destination.hash)) print(f"Listening on : {RNS.prettyhexrep(destination.hash)}")
exit(0) exit(0)
if disable_auth: if disable_auth:
@ -74,7 +74,7 @@ def listen(configdir, identitypath = None, verbosity = 0, quietness = 0, allowed
try: try:
dest_len = (RNS.Reticulum.TRUNCATED_HASHLENGTH//8)*2 dest_len = (RNS.Reticulum.TRUNCATED_HASHLENGTH//8)*2
if len(a) != dest_len: if len(a) != dest_len:
raise ValueError("Allowed destination length is invalid, must be {hex} hexadecimal characters ({byte} bytes).".format(hex=dest_len, byte=dest_len//2)) raise ValueError(f"Allowed destination length is invalid, must be {dest_len} hexadecimal characters ({dest_len // 2} bytes).")
try: try:
destination_hash = bytes.fromhex(a) destination_hash = bytes.fromhex(a)
allowed_identity_hashes.append(destination_hash) allowed_identity_hashes.append(destination_hash)
@ -103,7 +103,7 @@ def listen(configdir, identitypath = None, verbosity = 0, quietness = 0, allowed
allow = RNS.Destination.ALLOW_ALL, allow = RNS.Destination.ALLOW_ALL,
) )
RNS.log("rnx listening for commands on "+RNS.prettyhexrep(destination.hash)) RNS.log(f"rnx listening for commands on {RNS.prettyhexrep(destination.hash)}")
if not disable_announce: if not disable_announce:
destination.announce() destination.announce()
@ -114,16 +114,16 @@ def listen(configdir, identitypath = None, verbosity = 0, quietness = 0, allowed
def command_link_established(link): def command_link_established(link):
link.set_remote_identified_callback(initiator_identified) link.set_remote_identified_callback(initiator_identified)
link.set_link_closed_callback(command_link_closed) link.set_link_closed_callback(command_link_closed)
RNS.log("Command link "+str(link)+" established") RNS.log(f"Command link {link} established")
def command_link_closed(link): def command_link_closed(link):
RNS.log("Command link "+str(link)+" closed") RNS.log(f"Command link {link} closed")
def initiator_identified(link, identity): def initiator_identified(link, identity):
global allow_all global allow_all
RNS.log("Initiator of link "+str(link)+" identified as "+RNS.prettyhexrep(identity.hash)) RNS.log(f"Initiator of link {link} identified as {RNS.prettyhexrep(identity.hash)}")
if not allow_all and not identity.hash in allowed_identity_hashes: if not allow_all and not identity.hash in allowed_identity_hashes:
RNS.log("Identity "+RNS.prettyhexrep(identity.hash)+" not allowed, tearing down link") RNS.log(f"Identity {RNS.prettyhexrep(identity.hash)} not allowed, tearing down link")
link.teardown() link.teardown()
def execute_received_command(path, data, request_id, remote_identity, requested_at): def execute_received_command(path, data, request_id, remote_identity, requested_at):
@ -134,9 +134,9 @@ def execute_received_command(path, data, request_id, remote_identity, requested_
stdin = data[4] # Data passed to stdin stdin = data[4] # Data passed to stdin
if remote_identity != None: if remote_identity != None:
RNS.log("Executing command ["+command+"] for "+RNS.prettyhexrep(remote_identity.hash)) RNS.log(f"Executing command [{command}] for {RNS.prettyhexrep(remote_identity.hash)}")
else: else:
RNS.log("Executing command ["+command+"] for unknown requestor") RNS.log(f"Executing command [{command}] for unknown requestor")
result = [ result = [
False, # 0: Command was executed False, # 0: Command was executed
@ -178,7 +178,7 @@ def execute_received_command(path, data, request_id, remote_identity, requested_
pass pass
if timeout != None and time.time() > result[6]+timeout: if timeout != None and time.time() > result[6]+timeout:
RNS.log("Command ["+command+"] timed out and is being killed...") RNS.log(f"Command [{command}] timed out and is being killed...")
process.terminate() process.terminate()
process.wait() process.wait()
if process.poll() != None: if process.poll() != None:
@ -219,9 +219,9 @@ def execute_received_command(path, data, request_id, remote_identity, requested_
return result return result
if remote_identity != None: if remote_identity != None:
RNS.log("Delivering result of command ["+str(command)+"] to "+RNS.prettyhexrep(remote_identity.hash)) RNS.log(f"Delivering result of command [{command}] to {RNS.prettyhexrep(remote_identity.hash)}")
else: else:
RNS.log("Delivering result of command ["+str(command)+"] to unknown requestor") RNS.log(f"Delivering result of command [{command}] to unknown requestor")
return result return result
@ -231,14 +231,14 @@ def spin(until=None, msg=None, timeout=None):
if timeout != None: if timeout != None:
timeout = time.time()+timeout timeout = time.time()+timeout
print(msg+" ", end=" ") print(f"{msg} ", end=" ")
while (timeout == None or time.time()<timeout) and not until(): while (timeout == None or time.time()<timeout) and not until():
time.sleep(0.1) time.sleep(0.1)
print(("\b\b"+syms[i]+" "), end="") print(f"\b\b{syms[i]} ", end="")
sys.stdout.flush() sys.stdout.flush()
i = (i+1)%len(syms) i = (i+1)%len(syms)
print("\r"+" "*len(msg)+" \r", end="") print(f"\r{' ' * len(msg)} \r", end="")
if timeout != None and time.time() > timeout: if timeout != None and time.time() > timeout:
return False return False
@ -259,8 +259,8 @@ def spin_stat(until=None, timeout=None):
time.sleep(0.1) time.sleep(0.1)
prg = current_progress prg = current_progress
percent = round(prg * 100.0, 1) percent = round(prg * 100.0, 1)
stat_str = str(percent)+"% - " + size_str(int(prg*response_transfer_size)) + " of " + size_str(response_transfer_size) + " - " +size_str(speed, "b")+"ps" stat_str = f"{percent}% - {size_str(int(prg * response_transfer_size))} of {size_str(response_transfer_size)} - {size_str(speed, 'b')}ps"
print("\r \rReceiving result "+syms[i]+" "+stat_str, end=" ") print(f'\r \rReceiving result {syms[i]} {stat_str}', end=" ")
sys.stdout.flush() sys.stdout.flush()
i = (i+1)%len(syms) i = (i+1)%len(syms)
@ -303,7 +303,7 @@ def execute(configdir, identitypath = None, verbosity = 0, quietness = 0, detail
try: try:
dest_len = (RNS.Reticulum.TRUNCATED_HASHLENGTH//8)*2 dest_len = (RNS.Reticulum.TRUNCATED_HASHLENGTH//8)*2
if len(destination) != dest_len: if len(destination) != dest_len:
raise ValueError("Allowed destination length is invalid, must be {hex} hexadecimal characters ({byte} bytes).".format(hex=dest_len, byte=dest_len//2)) raise ValueError(f"Allowed destination length is invalid, must be {dest_len} hexadecimal characters ({dest_len // 2} bytes).")
try: try:
destination_hash = bytes.fromhex(destination) destination_hash = bytes.fromhex(destination)
except Exception as e: except Exception as e:
@ -321,7 +321,7 @@ def execute(configdir, identitypath = None, verbosity = 0, quietness = 0, detail
if not RNS.Transport.has_path(destination_hash): if not RNS.Transport.has_path(destination_hash):
RNS.Transport.request_path(destination_hash) RNS.Transport.request_path(destination_hash)
if not spin(until=lambda: RNS.Transport.has_path(destination_hash), msg="Path to "+RNS.prettyhexrep(destination_hash)+" requested", timeout=timeout): if not spin(until=lambda: RNS.Transport.has_path(destination_hash), msg=f"Path to {RNS.prettyhexrep(destination_hash)} requested", timeout=timeout):
print("Path not found") print("Path not found")
exit(242) exit(242)
@ -339,8 +339,8 @@ def execute(configdir, identitypath = None, verbosity = 0, quietness = 0, detail
link = RNS.Link(listener_destination) link = RNS.Link(listener_destination)
link.did_identify = False link.did_identify = False
if not spin(until=lambda: link.status == RNS.Link.ACTIVE, msg="Establishing link with "+RNS.prettyhexrep(destination_hash), timeout=timeout): if not spin(until=lambda: link.status == RNS.Link.ACTIVE, msg=f"Establishing link with {RNS.prettyhexrep(destination_hash)}", timeout=timeout):
print("Could not establish link with "+RNS.prettyhexrep(destination_hash)) print(f"Could not establish link with {RNS.prettyhexrep(destination_hash)}")
exit(243) exit(243)
if not noid and not link.did_identify: if not noid and not link.did_identify:
@ -443,7 +443,7 @@ def execute(configdir, identitypath = None, verbosity = 0, quietness = 0, detail
print("\n--- End of remote output, rnx done ---") print("\n--- End of remote output, rnx done ---")
if started != None and concluded != None: if started != None and concluded != None:
cmd_duration = round(concluded - started, 3) cmd_duration = round(concluded - started, 3)
print("Remote command execution took "+str(cmd_duration)+" seconds") print(f"Remote command execution took {cmd_duration} seconds")
total_size = request_receipt.response_size total_size = request_receipt.response_size
if request_receipt.request_size != None: if request_receipt.request_size != None:
@ -453,27 +453,27 @@ def execute(configdir, identitypath = None, verbosity = 0, quietness = 0, detail
if transfer_duration == 1: if transfer_duration == 1:
tdstr = " in 1 second" tdstr = " in 1 second"
elif transfer_duration < 10: elif transfer_duration < 10:
tdstr = " in "+str(transfer_duration)+" seconds" tdstr = f" in {transfer_duration} seconds"
else: else:
tdstr = " in "+pretty_time(transfer_duration) tdstr = f" in {pretty_time(transfer_duration)}"
spdstr = ", effective rate "+size_str(total_size/transfer_duration, "b")+"ps" spdstr = f", effective rate {size_str(total_size / transfer_duration, 'b')}ps"
print("Transferred "+size_str(total_size)+tdstr+spdstr) print(f"Transferred {size_str(total_size)}{tdstr}{spdstr}")
if outlen != None and stdout != None: if outlen != None and stdout != None:
if len(stdout) < outlen: if len(stdout) < outlen:
tstr = ", "+str(len(stdout))+" bytes displayed" tstr = f", {len(stdout)} bytes displayed"
else: else:
tstr = "" tstr = ""
print("Remote wrote "+str(outlen)+" bytes to stdout"+tstr) print(f"Remote wrote {outlen} bytes to stdout{tstr}")
if errlen != None and stderr != None: if errlen != None and stderr != None:
if len(stderr) < errlen: if len(stderr) < errlen:
tstr = ", "+str(len(stderr))+" bytes displayed" tstr = f", {len(stderr)} bytes displayed"
else: else:
tstr = "" tstr = ""
print("Remote wrote "+str(errlen)+" bytes to stderr"+tstr) print(f"Remote wrote {errlen} bytes to stderr{tstr}")
else: else:
if stdout != None and len(stdout) > 0: if stdout != None and len(stdout) > 0:
@ -487,9 +487,9 @@ def execute(configdir, identitypath = None, verbosity = 0, quietness = 0, detail
sys.stderr.flush() sys.stderr.flush()
print("\nOutput truncated before being returned:") print("\nOutput truncated before being returned:")
if len(stdout) != 0 and len(stdout) < outlen: if len(stdout) != 0 and len(stdout) < outlen:
print(" stdout truncated to "+str(len(stdout))+" bytes") print(f" stdout truncated to {len(stdout)} bytes")
if len(stderr) != 0 and len(stderr) < errlen: if len(stderr) != 0 and len(stderr) < errlen:
print(" stderr truncated to "+str(len(stderr))+" bytes") print(f" stderr truncated to {len(stderr)} bytes")
else: else:
print("Remote could not execute command") print("Remote could not execute command")
if interactive: if interactive:
@ -547,7 +547,7 @@ def main():
parser.add_argument("--stdin", action='store', default=None, help="pass input to stdin", type=str) parser.add_argument("--stdin", action='store', default=None, help="pass input to stdin", type=str)
parser.add_argument("--stdout", action='store', default=None, help="max size in bytes of returned stdout", type=int) parser.add_argument("--stdout", action='store', default=None, help="max size in bytes of returned stdout", type=int)
parser.add_argument("--stderr", action='store', default=None, help="max size in bytes of returned stderr", type=int) parser.add_argument("--stderr", action='store', default=None, help="max size in bytes of returned stderr", type=int)
parser.add_argument("--version", action="version", version="rnx {version}".format(version=__version__)) parser.add_argument("--version", action="version", version=f"rnx {__version__}")
args = parser.parse_args() args = parser.parse_args()
@ -593,7 +593,7 @@ def main():
while True: while True:
try: try:
cstr = str(code) if code and code != 0 else "" cstr = str(code) if code and code != 0 else ""
prompt = cstr+"> " prompt = f"{cstr}> "
print(prompt,end="") print(prompt,end="")
# cmdbuf = b"" # cmdbuf = b""
@ -661,12 +661,12 @@ def size_str(num, suffix='B'):
for unit in units: for unit in units:
if abs(num) < 1000.0: if abs(num) < 1000.0:
if unit == "": if unit == "":
return "%.0f %s%s" % (num, unit, suffix) return f"{num:.0f} {unit}{suffix}"
else: else:
return "%.2f %s%s" % (num, unit, suffix) return f"{num:.2f} {unit}{suffix}"
num /= 1000.0 num /= 1000.0
return "%.2f%s%s" % (num, last_unit, suffix) return f"{num:.2f}{last_unit}{suffix}"
def pretty_time(time, verbose=False): def pretty_time(time, verbose=False):
days = int(time // (24 * 3600)) days = int(time // (24 * 3600))
@ -684,16 +684,16 @@ def pretty_time(time, verbose=False):
components = [] components = []
if days > 0: if days > 0:
components.append(str(days)+" day"+sd if verbose else str(days)+"d") components.append(f"{days} day{sd}" if verbose else f"{days}d")
if hours > 0: if hours > 0:
components.append(str(hours)+" hour"+sh if verbose else str(hours)+"h") components.append(f"{hours} hour{sh}" if verbose else f"{hours}h")
if minutes > 0: if minutes > 0:
components.append(str(minutes)+" minute"+sm if verbose else str(minutes)+"m") components.append(f"{minutes} minute{sm}" if verbose else f"{minutes}m")
if seconds > 0: if seconds > 0:
components.append(str(seconds)+" second"+ss if verbose else str(seconds)+"s") components.append(f"{seconds} second{ss}" if verbose else f"{seconds}s")
i = 0 i = 0
tstr = "" tstr = ""

View File

@ -147,7 +147,7 @@ def rand():
def trace_exception(e): def trace_exception(e):
import traceback import traceback
exception_info = "".join(traceback.TracebackException.from_exception(e).format()) exception_info = "".join(traceback.TracebackException.from_exception(e).format())
log(f"An unhandled {str(type(e))} exception occurred: {str(e)}", LOG_ERROR) log(f"An unhandled {type(e)} exception occurred: {e}", LOG_ERROR)
log(exception_info, LOG_ERROR) log(exception_info, LOG_ERROR)
def hexrep(data, delimit=True): def hexrep(data, delimit=True):
@ -159,12 +159,12 @@ def hexrep(data, delimit=True):
delimiter = ":" delimiter = ":"
if not delimit: if not delimit:
delimiter = "" delimiter = ""
hexrep = delimiter.join("{:02x}".format(c) for c in data) hexrep = delimiter.join(f"{c:02x}" for c in data)
return hexrep return hexrep
def prettyhexrep(data): def prettyhexrep(data):
delimiter = "" delimiter = ""
hexrep = "<"+delimiter.join("{:02x}".format(c) for c in data)+">" hexrep = "<"+delimiter.join(f"{c:02x}" for c in data)+">"
return hexrep return hexrep
def prettyspeed(num, suffix="b"): def prettyspeed(num, suffix="b"):
@ -182,12 +182,12 @@ def prettysize(num, suffix='B'):
for unit in units: for unit in units:
if abs(num) < 1000.0: if abs(num) < 1000.0:
if unit == "": if unit == "":
return "%.0f %s%s" % (num, unit, suffix) return f"{num:.0f} {unit}{suffix}"
else: else:
return "%.2f %s%s" % (num, unit, suffix) return f"{num:.2f} {unit}{suffix}"
num /= 1000.0 num /= 1000.0
return "%.2f%s%s" % (num, last_unit, suffix) return f"{num:.2f}{last_unit}{suffix}"
def prettyfrequency(hz, suffix="Hz"): def prettyfrequency(hz, suffix="Hz"):
num = hz*1e6 num = hz*1e6
@ -196,10 +196,10 @@ def prettyfrequency(hz, suffix="Hz"):
for unit in units: for unit in units:
if abs(num) < 1000.0: if abs(num) < 1000.0:
return "%.2f %s%s" % (num, unit, suffix) return f"{num:.2f} {unit}{suffix}"
num /= 1000.0 num /= 1000.0
return "%.2f%s%s" % (num, last_unit, suffix) return f"{num:.2f}{last_unit}{suffix}"
def prettydistance(m, suffix="m"): def prettydistance(m, suffix="m"):
num = m*1e6 num = m*1e6
@ -212,10 +212,10 @@ def prettydistance(m, suffix="m"):
if unit == "c": divisor = 100 if unit == "c": divisor = 100
if abs(num) < divisor: if abs(num) < divisor:
return "%.2f %s%s" % (num, unit, suffix) return f"{num:.2f} {unit}{suffix}"
num /= divisor num /= divisor
return "%.2f %s%s" % (num, last_unit, suffix) return f"{num:.2f} {last_unit}{suffix}"
def prettytime(time, verbose=False, compact=False): def prettytime(time, verbose=False, compact=False):
days = int(time // (24 * 3600)) days = int(time // (24 * 3600))

View File

@ -1 +1 @@
__version__ = "0.8.3" __version__ = "0.8.4"

View File

@ -1,5 +1,5 @@
import os import os
import glob import glob
modules = glob.glob(os.path.dirname(__file__)+"/*.py") modules = glob.glob(f"{os.path.dirname(__file__)}/*.py")
__all__ = [ os.path.basename(f)[:-3] for f in modules if not f.endswith('__init__.py')] __all__ = [ os.path.basename(f)[:-3] for f in modules if not f.endswith('__init__.py')]

View File

@ -129,7 +129,7 @@ def getObj(s):
global compiler global compiler
if compiler is None: if compiler is None:
import compiler import compiler
s = "a=" + s s = f"a={s}"
p = compiler.parse(s) p = compiler.parse(s)
return p.getChildren()[1].getChildren()[0].getChildren()[1] return p.getChildren()[1].getChildren()[0].getChildren()[1]
@ -138,7 +138,7 @@ class UnknownType(Exception):
pass pass
class Builder(object): class Builder:
def build(self, o): def build(self, o):
if m is None: if m is None:
@ -261,7 +261,7 @@ class InterpolationLoopError(InterpolationError):
def __init__(self, option): def __init__(self, option):
InterpolationError.__init__( InterpolationError.__init__(
self, self,
'interpolation loop detected in value "%s".' % option) f'interpolation loop detected in value "{option}".')
class RepeatSectionError(ConfigObjError): class RepeatSectionError(ConfigObjError):
@ -274,7 +274,7 @@ class RepeatSectionError(ConfigObjError):
class MissingInterpolationOption(InterpolationError): class MissingInterpolationOption(InterpolationError):
"""A value specified for interpolation was missing.""" """A value specified for interpolation was missing."""
def __init__(self, option): def __init__(self, option):
msg = 'missing option "%s" in interpolation.' % option msg = f'missing option "{option}" in interpolation.'
InterpolationError.__init__(self, msg) InterpolationError.__init__(self, msg)
@ -283,7 +283,7 @@ class UnreprError(ConfigObjError):
class InterpolationEngine(object): class InterpolationEngine:
""" """
A helper class to help perform string interpolation. A helper class to help perform string interpolation.
@ -336,7 +336,7 @@ class InterpolationEngine(object):
replacement = recursive_interpolate(k, v, s, backtrail) replacement = recursive_interpolate(k, v, s, backtrail)
# Replace the matched string with its final value # Replace the matched string with its final value
start, end = match.span() start, end = match.span()
value = ''.join((value[:start], replacement, value[end:])) value = f"{value[:start]}{replacement}{value[end:]}"
new_search_start = start + len(replacement) new_search_start = start + len(replacement)
# Pick up the next interpolation key, if any, for next time # Pick up the next interpolation key, if any, for next time
# through the while loop # through the while loop
@ -553,11 +553,11 @@ class Section(dict):
"""Fetch the item and do string interpolation.""" """Fetch the item and do string interpolation."""
val = dict.__getitem__(self, key) val = dict.__getitem__(self, key)
if self.main.interpolation: if self.main.interpolation:
if isinstance(val, six.string_types): if isinstance(val, str):
return self._interpolate(key, val) return self._interpolate(key, val)
if isinstance(val, list): if isinstance(val, list):
def _check(entry): def _check(entry):
if isinstance(entry, six.string_types): if isinstance(entry, str):
return self._interpolate(key, entry) return self._interpolate(key, entry)
return entry return entry
new = [_check(entry) for entry in val] new = [_check(entry) for entry in val]
@ -580,8 +580,8 @@ class Section(dict):
``unrepr`` must be set when setting a value to a dictionary, without ``unrepr`` must be set when setting a value to a dictionary, without
creating a new sub-section. creating a new sub-section.
""" """
if not isinstance(key, six.string_types): if not isinstance(key, str):
raise ValueError('The key "%s" is not a string.' % key) raise ValueError(f'The key "{key}" is not a string.')
# add the comment # add the comment
if key not in self.comments: if key not in self.comments:
@ -614,14 +614,14 @@ class Section(dict):
if key not in self: if key not in self:
self.scalars.append(key) self.scalars.append(key)
if not self.main.stringify: if not self.main.stringify:
if isinstance(value, six.string_types): if isinstance(value, str):
pass pass
elif isinstance(value, (list, tuple)): elif isinstance(value, (list, tuple)):
for entry in value: for entry in value:
if not isinstance(entry, six.string_types): if not isinstance(entry, str):
raise TypeError('Value is not a string "%s".' % entry) raise TypeError(f'Value is not a string "{entry}".')
else: else:
raise TypeError('Value is not a string "%s".' % value) raise TypeError(f'Value is not a string "{value}".')
dict.__setitem__(self, key, value) dict.__setitem__(self, key, value)
@ -728,7 +728,7 @@ class Section(dict):
def iterkeys(self): def iterkeys(self):
"""D.iterkeys() -> an iterator over the keys of D""" """D.iterkeys() -> an iterator over the keys of D"""
return iter((self.scalars + self.sections)) return iter(self.scalars + self.sections)
__iter__ = iterkeys __iter__ = iterkeys
@ -745,7 +745,7 @@ class Section(dict):
return self[key] return self[key]
except MissingInterpolationOption: except MissingInterpolationOption:
return dict.__getitem__(self, key) return dict.__getitem__(self, key)
return '{%s}' % ', '.join([('%s: %s' % (repr(key), repr(_getval(key)))) return '{%s}' % ', '.join([f'{key!r}: {_getval(key)!r}'
for key in (self.scalars + self.sections)]) for key in (self.scalars + self.sections)])
__str__ = __repr__ __str__ = __repr__
@ -823,7 +823,7 @@ class Section(dict):
elif oldkey in self.sections: elif oldkey in self.sections:
the_list = self.sections the_list = self.sections
else: else:
raise KeyError('Key "%s" not found.' % oldkey) raise KeyError(f'Key "{oldkey}" not found.')
pos = the_list.index(oldkey) pos = the_list.index(oldkey)
# #
val = self[oldkey] val = self[oldkey]
@ -959,13 +959,13 @@ class Section(dict):
return False return False
else: else:
try: try:
if not isinstance(val, six.string_types): if not isinstance(val, str):
# TODO: Why do we raise a KeyError here? # TODO: Why do we raise a KeyError here?
raise KeyError() raise KeyError()
else: else:
return self.main._bools[val.lower()] return self.main._bools[val.lower()]
except KeyError: except KeyError:
raise ValueError('Value "%s" is neither True nor False' % val) raise ValueError(f'Value "{val}" is neither True nor False')
def as_int(self, key): def as_int(self, key):
@ -1210,7 +1210,7 @@ class ConfigObj(Section):
# TODO: check the values too. # TODO: check the values too.
for entry in options: for entry in options:
if entry not in OPTION_DEFAULTS: if entry not in OPTION_DEFAULTS:
raise TypeError('Unrecognised option "%s".' % entry) raise TypeError(f'Unrecognised option "{entry}".')
for entry, value in list(OPTION_DEFAULTS.items()): for entry, value in list(OPTION_DEFAULTS.items()):
if entry not in options: if entry not in options:
options[entry] = value options[entry] = value
@ -1230,14 +1230,14 @@ class ConfigObj(Section):
def _load(self, infile, configspec): def _load(self, infile, configspec):
if isinstance(infile, six.string_types): if isinstance(infile, str):
self.filename = infile self.filename = infile
if os.path.isfile(infile): if os.path.isfile(infile):
with open(infile, 'rb') as h: with open(infile, 'rb') as h:
content = h.readlines() or [] content = h.readlines() or []
elif self.file_error: elif self.file_error:
# raise an error if the file doesn't exist # raise an error if the file doesn't exist
raise IOError('Config file not found: "%s".' % self.filename) raise OSError(f'Config file not found: "{self.filename}".')
else: else:
# file doesn't already exist # file doesn't already exist
if self.create_empty: if self.create_empty:
@ -1298,15 +1298,15 @@ class ConfigObj(Section):
break break
break break
assert all(isinstance(line, six.string_types) for line in content), repr(content) assert all(isinstance(line, str) for line in content), repr(content)
content = [line.rstrip('\r\n') for line in content] content = [line.rstrip('\r\n') for line in content]
self._parse(content) self._parse(content)
# if we had any errors, now is the time to raise them # if we had any errors, now is the time to raise them
if self._errors: if self._errors:
info = "at line %s." % self._errors[0].line_number info = f"at line {self._errors[0].line_number}."
if len(self._errors) > 1: if len(self._errors) > 1:
msg = "Parsing failed with several errors.\nFirst error %s" % info msg = f"Parsing failed with several errors.\nFirst error {info}"
error = ConfigObjError(msg) error = ConfigObjError(msg)
else: else:
error = self._errors[0] error = self._errors[0]
@ -1363,9 +1363,7 @@ class ConfigObj(Section):
return self[key] return self[key]
except MissingInterpolationOption: except MissingInterpolationOption:
return dict.__getitem__(self, key) return dict.__getitem__(self, key)
return ('ConfigObj({%s})' % return ('ConfigObj({%s})' % ', '.join([f'{key!r}: {_getval(key)!r}' for key in (self.scalars + self.sections)]))
', '.join([('%s: %s' % (repr(key), repr(_getval(key))))
for key in (self.scalars + self.sections)]))
def _handle_bom(self, infile): def _handle_bom(self, infile):
@ -1403,7 +1401,7 @@ class ConfigObj(Section):
else: else:
line = infile line = infile
if isinstance(line, six.text_type): if isinstance(line, str):
# it's already decoded and there's no need to do anything # it's already decoded and there's no need to do anything
# else, just use the _decode utility method to handle # else, just use the _decode utility method to handle
# listifying appropriately # listifying appropriately
@ -1448,7 +1446,7 @@ class ConfigObj(Section):
# No encoding specified - so we need to check for UTF8/UTF16 # No encoding specified - so we need to check for UTF8/UTF16
for BOM, (encoding, final_encoding) in list(BOMS.items()): for BOM, (encoding, final_encoding) in list(BOMS.items()):
if not isinstance(line, six.binary_type) or not line.startswith(BOM): if not isinstance(line, bytes) or not line.startswith(BOM):
# didn't specify a BOM, or it's not a bytestring # didn't specify a BOM, or it's not a bytestring
continue continue
else: else:
@ -1464,9 +1462,9 @@ class ConfigObj(Section):
else: else:
infile = newline infile = newline
# UTF-8 # UTF-8
if isinstance(infile, six.text_type): if isinstance(infile, str):
return infile.splitlines(True) return infile.splitlines(True)
elif isinstance(infile, six.binary_type): elif isinstance(infile, bytes):
return infile.decode('utf-8').splitlines(True) return infile.decode('utf-8').splitlines(True)
else: else:
return self._decode(infile, 'utf-8') return self._decode(infile, 'utf-8')
@ -1479,7 +1477,7 @@ class ConfigObj(Section):
# returning a bytestring is fine # returning a bytestring is fine
return self._decode(infile, None) return self._decode(infile, None)
# No BOM discovered and no encoding specified, default to UTF-8 # No BOM discovered and no encoding specified, default to UTF-8
if isinstance(infile, six.binary_type): if isinstance(infile, bytes):
return infile.decode('utf-8').splitlines(True) return infile.decode('utf-8').splitlines(True)
else: else:
return self._decode(infile, 'utf-8') return self._decode(infile, 'utf-8')
@ -1487,7 +1485,7 @@ class ConfigObj(Section):
def _a_to_u(self, aString): def _a_to_u(self, aString):
"""Decode ASCII strings to unicode if a self.encoding is specified.""" """Decode ASCII strings to unicode if a self.encoding is specified."""
if isinstance(aString, six.binary_type) and self.encoding: if isinstance(aString, bytes) and self.encoding:
return aString.decode(self.encoding) return aString.decode(self.encoding)
else: else:
return aString return aString
@ -1499,9 +1497,9 @@ class ConfigObj(Section):
if is a string, it also needs converting to a list. if is a string, it also needs converting to a list.
""" """
if isinstance(infile, six.string_types): if isinstance(infile, str):
return infile.splitlines(True) return infile.splitlines(True)
if isinstance(infile, six.binary_type): if isinstance(infile, bytes):
# NOTE: Could raise a ``UnicodeDecodeError`` # NOTE: Could raise a ``UnicodeDecodeError``
if encoding: if encoding:
return infile.decode(encoding).splitlines(True) return infile.decode(encoding).splitlines(True)
@ -1510,7 +1508,7 @@ class ConfigObj(Section):
if encoding: if encoding:
for i, line in enumerate(infile): for i, line in enumerate(infile):
if isinstance(line, six.binary_type): if isinstance(line, bytes):
# NOTE: The isinstance test here handles mixed lists of unicode/string # NOTE: The isinstance test here handles mixed lists of unicode/string
# NOTE: But the decode will break on any non-string values # NOTE: But the decode will break on any non-string values
# NOTE: Or could raise a ``UnicodeDecodeError`` # NOTE: Or could raise a ``UnicodeDecodeError``
@ -1520,7 +1518,7 @@ class ConfigObj(Section):
def _decode_element(self, line): def _decode_element(self, line):
"""Decode element to unicode if necessary.""" """Decode element to unicode if necessary."""
if isinstance(line, six.binary_type) and self.default_encoding: if isinstance(line, bytes) and self.default_encoding:
return line.decode(self.default_encoding) return line.decode(self.default_encoding)
else: else:
return line return line
@ -1532,7 +1530,7 @@ class ConfigObj(Section):
Used by ``stringify`` within validate, to turn non-string values Used by ``stringify`` within validate, to turn non-string values
into strings. into strings.
""" """
if not isinstance(value, six.string_types): if not isinstance(value, str):
# intentially 'str' because it's just whatever the "normal" # intentially 'str' because it's just whatever the "normal"
# string type is for the python version we're dealing with # string type is for the python version we're dealing with
return str(value) return str(value)
@ -1627,7 +1625,7 @@ class ConfigObj(Section):
mat = self._keyword.match(line) mat = self._keyword.match(line)
if mat is None: if mat is None:
self._handle_error( self._handle_error(
'Invalid line ({0!r}) (matched as neither section nor keyword)'.format(line), f'Invalid line ({line!r}) (matched as neither section nor keyword)',
ParseError, infile, cur_index) ParseError, infile, cur_index)
else: else:
# is a keyword value # is a keyword value
@ -1735,7 +1733,7 @@ class ConfigObj(Section):
""" """
line = infile[cur_index] line = infile[cur_index]
cur_index += 1 cur_index += 1
message = '{0} at line {1}.'.format(text, cur_index) message = f'{text} at line {cur_index}.'
error = ErrorClass(message, cur_index, line) error = ErrorClass(message, cur_index, line)
if self.raise_errors: if self.raise_errors:
# raise the error - parsing stops here # raise the error - parsing stops here
@ -1783,16 +1781,16 @@ class ConfigObj(Section):
if not value: if not value:
return ',' return ','
elif len(value) == 1: elif len(value) == 1:
return self._quote(value[0], multiline=False) + ',' return f"{self._quote(value[0], multiline=False)},"
return ', '.join([self._quote(val, multiline=False) return ', '.join([self._quote(val, multiline=False)
for val in value]) for val in value])
if not isinstance(value, six.string_types): if not isinstance(value, str):
if self.stringify: if self.stringify:
# intentially 'str' because it's just whatever the "normal" # intentially 'str' because it's just whatever the "normal"
# string type is for the python version we're dealing with # string type is for the python version we're dealing with
value = str(value) value = str(value)
else: else:
raise TypeError('Value "%s" is not a string.' % value) raise TypeError(f'Value "{value}" is not a string.')
if not value: if not value:
return '""' return '""'
@ -1809,7 +1807,7 @@ class ConfigObj(Section):
# for normal values either single or double quotes will do # for normal values either single or double quotes will do
elif '\n' in value: elif '\n' in value:
# will only happen if multiline is off - e.g. '\n' in key # will only happen if multiline is off - e.g. '\n' in key
raise ConfigObjError('Value "%s" cannot be safely quoted.' % value) raise ConfigObjError(f'Value "{value}" cannot be safely quoted.')
elif ((value[0] not in wspace_plus) and elif ((value[0] not in wspace_plus) and
(value[-1] not in wspace_plus) and (value[-1] not in wspace_plus) and
(',' not in value)): (',' not in value)):
@ -1828,7 +1826,7 @@ class ConfigObj(Section):
def _get_single_quote(self, value): def _get_single_quote(self, value):
if ("'" in value) and ('"' in value): if ("'" in value) and ('"' in value):
raise ConfigObjError('Value "%s" cannot be safely quoted.' % value) raise ConfigObjError(f'Value "{value}" cannot be safely quoted.')
elif '"' in value: elif '"' in value:
quot = squot quot = squot
else: else:
@ -1838,7 +1836,7 @@ class ConfigObj(Section):
def _get_triple_quote(self, value): def _get_triple_quote(self, value):
if (value.find('"""') != -1) and (value.find("'''") != -1): if (value.find('"""') != -1) and (value.find("'''") != -1):
raise ConfigObjError('Value "%s" cannot be safely quoted.' % value) raise ConfigObjError(f'Value "{value}" cannot be safely quoted.')
if value.find('"""') == -1: if value.find('"""') == -1:
quot = tdquot quot = tdquot
else: else:
@ -1943,9 +1941,9 @@ class ConfigObj(Section):
except ConfigObjError as e: except ConfigObjError as e:
# FIXME: Should these errors have a reference # FIXME: Should these errors have a reference
# to the already parsed ConfigObj ? # to the already parsed ConfigObj ?
raise ConfigspecError('Parsing configspec failed: %s' % e) raise ConfigspecError(f'Parsing configspec failed: {e}')
except IOError as e: except OSError as e:
raise IOError('Reading configspec failed: %s' % e) raise OSError(f'Reading configspec failed: {e}')
self.configspec = configspec self.configspec = configspec
@ -1986,20 +1984,12 @@ class ConfigObj(Section):
val = self._decode_element(self._quote(this_entry)) val = self._decode_element(self._quote(this_entry))
else: else:
val = repr(this_entry) val = repr(this_entry)
return '%s%s%s%s%s' % (indent_string, return f"{indent_string}{self._decode_element(self._quote(entry, multiline=False))}{self._a_to_u(' = ')}{val}{self._decode_element(comment)}"
self._decode_element(self._quote(entry, multiline=False)),
self._a_to_u(' = '),
val,
self._decode_element(comment))
def _write_marker(self, indent_string, depth, entry, comment): def _write_marker(self, indent_string, depth, entry, comment):
"""Write a section marker line""" """Write a section marker line"""
return '%s%s%s%s%s' % (indent_string, return f"{indent_string}{self._a_to_u('[' * depth)}{self._quote(self._decode_element(entry), multiline=False)}{self._a_to_u(']' * depth)}{self._decode_element(comment)}"
self._a_to_u('[' * depth),
self._quote(self._decode_element(entry), multiline=False),
self._a_to_u(']' * depth),
self._decode_element(comment))
def _handle_comment(self, comment): def _handle_comment(self, comment):
@ -2111,7 +2101,7 @@ class ConfigObj(Section):
if not output.endswith(newline): if not output.endswith(newline):
output += newline output += newline
if isinstance(output, six.binary_type): if isinstance(output, bytes):
output_bytes = output output_bytes = output
else: else:
output_bytes = output.encode(self.encoding or output_bytes = output.encode(self.encoding or
@ -2284,7 +2274,7 @@ class ConfigObj(Section):
out[entry] = False out[entry] = False
else: else:
ret_false = False ret_false = False
msg = 'Value %r was provided as a section' % entry msg = f'Value {entry!r} was provided as a section'
out[entry] = validator.baseErrorClass(msg) out[entry] = validator.baseErrorClass(msg)
for entry in incorrect_sections: for entry in incorrect_sections:
ret_true = False ret_true = False
@ -2292,7 +2282,7 @@ class ConfigObj(Section):
out[entry] = False out[entry] = False
else: else:
ret_false = False ret_false = False
msg = 'Section %r was provided as a single value' % entry msg = f'Section {entry!r} was provided as a single value'
out[entry] = validator.baseErrorClass(msg) out[entry] = validator.baseErrorClass(msg)
# Missing sections will have been created as empty ones when the # Missing sections will have been created as empty ones when the
@ -2353,7 +2343,7 @@ class ConfigObj(Section):
This method raises a ``ReloadError`` if the ConfigObj doesn't have This method raises a ``ReloadError`` if the ConfigObj doesn't have
a filename attribute pointing to a file. a filename attribute pointing to a file.
""" """
if not isinstance(self.filename, six.string_types): if not isinstance(self.filename, str):
raise ReloadError() raise ReloadError()
filename = self.filename filename = self.filename
@ -2372,7 +2362,7 @@ class ConfigObj(Section):
class SimpleVal(object): class SimpleVal:
""" """
A simple validator. A simple validator.
Can be used to check that all members expected are present. Can be used to check that all members expected are present.

View File

@ -11,7 +11,7 @@ def parse_reply(data):
try: try:
msg = sam.Message(data.decode().strip()) msg = sam.Message(data.decode().strip())
logger.debug("SAM reply: "+str(msg)) logger.debug(f"SAM reply: {msg}")
except: except:
raise ConnectionAbortedError("Invalid SAM response") raise ConnectionAbortedError("Invalid SAM response")
@ -89,7 +89,7 @@ async def create_session(session_name, sam_address=sam.DEFAULT_ADDRESS,
:param options: (optional) A dict object with i2cp options :param options: (optional) A dict object with i2cp options
:return: A (reader, writer) pair :return: A (reader, writer) pair
""" """
logger.debug("Creating session {}".format(session_name)) logger.debug(f"Creating session {session_name}")
if destination: if destination:
if type(destination) == sam.Destination: if type(destination) == sam.Destination:
destination = destination destination = destination
@ -101,7 +101,7 @@ async def create_session(session_name, sam_address=sam.DEFAULT_ADDRESS,
else: else:
dest_string = sam.TRANSIENT_DESTINATION dest_string = sam.TRANSIENT_DESTINATION
options = " ".join(["{}={}".format(k, v) for k, v in options.items()]) options = " ".join([f"{k}={v}" for k, v in options.items()])
reader, writer = await get_sam_socket(sam_address, loop) reader, writer = await get_sam_socket(sam_address, loop)
writer.write(sam.session_create( writer.write(sam.session_create(
@ -113,7 +113,7 @@ async def create_session(session_name, sam_address=sam.DEFAULT_ADDRESS,
destination = sam.Destination( destination = sam.Destination(
reply["DESTINATION"], has_private_key=True) reply["DESTINATION"], has_private_key=True)
logger.debug(destination.base32) logger.debug(destination.base32)
logger.debug("Session created {}".format(session_name)) logger.debug(f"Session created {session_name}")
return (reader, writer) return (reader, writer)
else: else:
writer.close() writer.close()
@ -129,7 +129,7 @@ async def stream_connect(session_name, destination,
:param loop: (optional) Event loop instance :param loop: (optional) Event loop instance
:return: A (reader, writer) pair :return: A (reader, writer) pair
""" """
logger.debug("Connecting stream {}".format(session_name)) logger.debug(f"Connecting stream {session_name}")
if isinstance(destination, str) and not destination.endswith(".i2p"): if isinstance(destination, str) and not destination.endswith(".i2p"):
destination = sam.Destination(destination) destination = sam.Destination(destination)
elif isinstance(destination, str): elif isinstance(destination, str):
@ -140,7 +140,7 @@ async def stream_connect(session_name, destination,
silent="false")) silent="false"))
reply = parse_reply(await reader.readline()) reply = parse_reply(await reader.readline())
if reply.ok: if reply.ok:
logger.debug("Stream connected {}".format(session_name)) logger.debug(f"Stream connected {session_name}")
return (reader, writer) return (reader, writer)
else: else:
writer.close() writer.close()

View File

@ -23,7 +23,7 @@ TRANSIENT_DESTINATION = "TRANSIENT"
VALID_BASE32_ADDRESS = re.compile(r"^([a-zA-Z0-9]{52}).b32.i2p$") VALID_BASE32_ADDRESS = re.compile(r"^([a-zA-Z0-9]{52}).b32.i2p$")
VALID_BASE64_ADDRESS = re.compile(r"^([a-zA-Z0-9-~=]{516,528})$") VALID_BASE64_ADDRESS = re.compile(r"^([a-zA-Z0-9-~=]{516,528})$")
class Message(object): class Message:
"""Parse SAM message to an object""" """Parse SAM message to an object"""
def __init__(self, s): def __init__(self, s):
self.opts = {} self.opts = {}
@ -51,34 +51,30 @@ class Message(object):
# SAM request messages # SAM request messages
def hello(min_version, max_version): def hello(min_version, max_version):
return "HELLO VERSION MIN={} MAX={}\n".format(min_version, return f"HELLO VERSION MIN={min_version} MAX={max_version}\n".encode()
max_version).encode()
def session_create(style, session_id, destination, options=""): def session_create(style, session_id, destination, options=""):
return "SESSION CREATE STYLE={} ID={} DESTINATION={} {}\n".format( return f"SESSION CREATE STYLE={style} ID={session_id} DESTINATION={destination} {options}\n".encode()
style, session_id, destination, options).encode()
def stream_connect(session_id, destination, silent="false"): def stream_connect(session_id, destination, silent="false"):
return "STREAM CONNECT ID={} DESTINATION={} SILENT={}\n".format( return f"STREAM CONNECT ID={session_id} DESTINATION={destination} SILENT={silent}\n".encode()
session_id, destination, silent).encode()
def stream_accept(session_id, silent="false"): def stream_accept(session_id, silent="false"):
return "STREAM ACCEPT ID={} SILENT={}\n".format(session_id, silent).encode() return f"STREAM ACCEPT ID={session_id} SILENT={silent}\n".encode()
def stream_forward(session_id, port, options=""): def stream_forward(session_id, port, options=""):
return "STREAM FORWARD ID={} PORT={} {}\n".format( return f"STREAM FORWARD ID={session_id} PORT={port} {options}\n".encode()
session_id, port, options).encode()
def naming_lookup(name): def naming_lookup(name):
return "NAMING LOOKUP NAME={}\n".format(name).encode() return f"NAMING LOOKUP NAME={name}\n".encode()
def dest_generate(signature_type): def dest_generate(signature_type):
return "DEST GENERATE SIGNATURE_TYPE={}\n".format(signature_type).encode() return f"DEST GENERATE SIGNATURE_TYPE={signature_type}\n".encode()
class Destination(object): class Destination:
"""I2P destination """I2P destination
https://geti2p.net/spec/common-structures#destination https://geti2p.net/spec/common-structures#destination
@ -101,7 +97,7 @@ class Destination(object):
def __init__(self, data=None, path=None, has_private_key=False): def __init__(self, data=None, path=None, has_private_key=False):
#: Binary destination #: Binary destination
self.data = bytes() self.data = b''
#: Base64 encoded destination #: Base64 encoded destination
self.base64 = "" self.base64 = ""
#: :class:`RNS.vendor.i2plib.PrivateKey` instance or None #: :class:`RNS.vendor.i2plib.PrivateKey` instance or None
@ -123,7 +119,7 @@ class Destination(object):
self.base64 = data if type(data) == str else i2p_b64encode(data) self.base64 = data if type(data) == str else i2p_b64encode(data)
def __repr__(self): def __repr__(self):
return "<Destination: {}>".format(self.base32) return f"<Destination: {self.base32}>"
@property @property
def base32(self): def base32(self):
@ -131,7 +127,7 @@ class Destination(object):
desthash = sha256(self.data).digest() desthash = sha256(self.data).digest()
return b32encode(desthash).decode()[:52].lower() return b32encode(desthash).decode()[:52].lower()
class PrivateKey(object): class PrivateKey:
"""I2P private key """I2P private key
https://geti2p.net/spec/common-structures#keysandcert https://geti2p.net/spec/common-structures#keysandcert

View File

@ -18,7 +18,7 @@ async def proxy_data(reader, writer):
break break
writer.write(data) writer.write(data)
except Exception as e: except Exception as e:
logger.debug('proxy_data_task exception {}'.format(e)) logger.debug(f'proxy_data_task exception {e}')
finally: finally:
try: try:
writer.close() writer.close()
@ -26,7 +26,7 @@ async def proxy_data(reader, writer):
pass pass
logger.debug('close connection') logger.debug('close connection')
class I2PTunnel(object): class I2PTunnel:
"""Base I2P Tunnel object, not to be used directly """Base I2P Tunnel object, not to be used directly
:param local_address: A local address to use for a tunnel. :param local_address: A local address to use for a tunnel.
@ -141,8 +141,7 @@ class ServerTunnel(I2PTunnel):
# data and dest may come in one chunk # data and dest may come in one chunk
dest, data = incoming.split(b"\n", 1) dest, data = incoming.split(b"\n", 1)
remote_destination = sam.Destination(dest.decode()) remote_destination = sam.Destination(dest.decode())
logger.debug("{} client connected: {}.b32.i2p".format( logger.debug(f"{self.session_name} client connected: {remote_destination.base32}.b32.i2p")
self.session_name, remote_destination.base32))
except Exception as e: except Exception as e:
self.status["exception"] = e self.status["exception"] = e

View File

@ -38,5 +38,5 @@ def generate_session_id(length=6):
"""Generate random session id""" """Generate random session id"""
rand = random.SystemRandom() rand = random.SystemRandom()
sid = [rand.choice(string.ascii_letters) for _ in range(length)] sid = [rand.choice(string.ascii_letters) for _ in range(length)]
return "reticulum-" + "".join(sid) return f"reticulum-{''.join(sid)}"

View File

@ -28,6 +28,6 @@ if os.name == "nt":
elif os.name == "posix": elif os.name == "posix":
from RNS.vendor.ifaddr._posix import get_adapters from RNS.vendor.ifaddr._posix import get_adapters
else: else:
raise RuntimeError("Unsupported Operating System: %s" % os.name) raise RuntimeError(f"Unsupported Operating System: {os.name}")
__all__ = ['Adapter', 'IP', 'get_adapters'] __all__ = ['Adapter', 'IP', 'get_adapters']

View File

@ -79,7 +79,7 @@ def get_adapters(include_unconfigured: bool = False) -> Iterable[shared.Adapter]
prefixlen = shared.ipv6_prefixlength(ipaddress.IPv6Address(netmaskStr)) prefixlen = shared.ipv6_prefixlength(ipaddress.IPv6Address(netmaskStr))
else: else:
assert netmask is not None, f'sockaddr_to_ip({addr[0].ifa_netmask}) returned None' assert netmask is not None, f'sockaddr_to_ip({addr[0].ifa_netmask}) returned None'
netmaskStr = str('0.0.0.0/' + netmask) netmaskStr = str(f"0.0.0.0/{netmask}")
prefixlen = ipaddress.IPv4Network(netmaskStr).prefixlen prefixlen = ipaddress.IPv4Network(netmaskStr).prefixlen
ip = shared.IP(ip_addr, prefixlen, name) ip = shared.IP(ip_addr, prefixlen, name)
add_ip(name, ip) add_ip(name, ip)

View File

@ -26,7 +26,7 @@ import platform
from typing import List, Optional, Tuple, Union from typing import List, Optional, Tuple, Union
class Adapter(object): class Adapter:
""" """
Represents a network interface device controller (NIC), such as a Represents a network interface device controller (NIC), such as a
network card. An adapter can have multiple IPs. network card. An adapter can have multiple IPs.
@ -58,9 +58,7 @@ class Adapter(object):
self.index = index self.index = index
def __repr__(self) -> str: def __repr__(self) -> str:
return "Adapter(name={name}, nice_name={nice_name}, ips={ips}, index={index})".format( return f"Adapter(name={self.name!r}, nice_name={self.nice_name!r}, ips={self.ips!r}, index={self.index!r})"
name=repr(self.name), nice_name=repr(self.nice_name), ips=repr(self.ips), index=repr(self.index)
)
# Type of an IPv4 address (a string in "xxx.xxx.xxx.xxx" format) # Type of an IPv4 address (a string in "xxx.xxx.xxx.xxx" format)
@ -70,7 +68,7 @@ _IPv4Address = str
_IPv6Address = Tuple[str, int, int] _IPv6Address = Tuple[str, int, int]
class IP(object): class IP:
""" """
Represents an IP address of an adapter. Represents an IP address of an adapter.
""" """
@ -112,9 +110,7 @@ class IP(object):
return isinstance(self.ip, tuple) return isinstance(self.ip, tuple)
def __repr__(self) -> str: def __repr__(self) -> str:
return "IP(ip={ip}, network_prefix={network_prefix}, nice_name={nice_name})".format( return f"IP(ip={self.ip!r}, network_prefix={self.network_prefix!r}, nice_name={self.nice_name!r})"
ip=repr(self.ip), network_prefix=repr(self.network_prefix), nice_name=repr(self.nice_name)
)
if platform.system() == "Darwin" or "BSD" in platform.system(): if platform.system() == "Darwin" or "BSD" in platform.system():

View File

@ -40,7 +40,7 @@ def ifaddresses(ifname) -> dict:
for ip in a.ips: for ip in a.ips:
t = {} t = {}
if ip.is_IPv4: if ip.is_IPv4:
net = ipaddress.ip_network(str(ip.ip)+"/"+str(ip.network_prefix), strict=False) net = ipaddress.ip_network(f"{ip.ip}/{ip.network_prefix}", strict=False)
t["addr"] = ip.ip t["addr"] = ip.ip
t["prefix"] = ip.network_prefix t["prefix"] = ip.network_prefix
t["broadcast"] = str(net.broadcast_address) t["broadcast"] = str(net.broadcast_address)

55
RNS/vendor/six.py vendored
View File

@ -20,7 +20,6 @@
"""Utilities for writing code that runs on Python 2 and 3""" """Utilities for writing code that runs on Python 2 and 3"""
from __future__ import absolute_import
import functools import functools
import itertools import itertools
@ -57,7 +56,7 @@ else:
MAXSIZE = int((1 << 31) - 1) MAXSIZE = int((1 << 31) - 1)
else: else:
# It's possible to have sizeof(long) != sizeof(Py_ssize_t). # It's possible to have sizeof(long) != sizeof(Py_ssize_t).
class X(object): class X:
def __len__(self): def __len__(self):
return 1 << 31 return 1 << 31
@ -88,7 +87,7 @@ def _import_module(name):
return sys.modules[name] return sys.modules[name]
class _LazyDescr(object): class _LazyDescr:
def __init__(self, name): def __init__(self, name):
self.name = name self.name = name
@ -108,7 +107,7 @@ class _LazyDescr(object):
class MovedModule(_LazyDescr): class MovedModule(_LazyDescr):
def __init__(self, name, old, new=None): def __init__(self, name, old, new=None):
super(MovedModule, self).__init__(name) super().__init__(name)
if PY3: if PY3:
if new is None: if new is None:
new = name new = name
@ -129,7 +128,7 @@ class MovedModule(_LazyDescr):
class _LazyModule(types.ModuleType): class _LazyModule(types.ModuleType):
def __init__(self, name): def __init__(self, name):
super(_LazyModule, self).__init__(name) super().__init__(name)
self.__doc__ = self.__class__.__doc__ self.__doc__ = self.__class__.__doc__
def __dir__(self): def __dir__(self):
@ -144,7 +143,7 @@ class _LazyModule(types.ModuleType):
class MovedAttribute(_LazyDescr): class MovedAttribute(_LazyDescr):
def __init__(self, name, old_mod, new_mod, old_attr=None, new_attr=None): def __init__(self, name, old_mod, new_mod, old_attr=None, new_attr=None):
super(MovedAttribute, self).__init__(name) super().__init__(name)
if PY3: if PY3:
if new_mod is None: if new_mod is None:
new_mod = name new_mod = name
@ -166,7 +165,7 @@ class MovedAttribute(_LazyDescr):
return getattr(module, self.attr) return getattr(module, self.attr)
class _SixMetaPathImporter(object): class _SixMetaPathImporter:
""" """
A meta path importer to import six.moves and its submodules. A meta path importer to import six.moves and its submodules.
@ -181,10 +180,10 @@ class _SixMetaPathImporter(object):
def _add_module(self, mod, *fullnames): def _add_module(self, mod, *fullnames):
for fullname in fullnames: for fullname in fullnames:
self.known_modules[self.name + "." + fullname] = mod self.known_modules[f"{self.name}.{fullname}"] = mod
def _get_module(self, fullname): def _get_module(self, fullname):
return self.known_modules[self.name + "." + fullname] return self.known_modules[f"{self.name}.{fullname}"]
def find_module(self, fullname, path=None): def find_module(self, fullname, path=None):
if fullname in self.known_modules: if fullname in self.known_modules:
@ -200,7 +199,7 @@ class _SixMetaPathImporter(object):
try: try:
return self.known_modules[fullname] return self.known_modules[fullname]
except KeyError: except KeyError:
raise ImportError("This loader does not know module " + fullname) raise ImportError(f"This loader does not know module {fullname}")
def load_module(self, fullname): def load_module(self, fullname):
try: try:
@ -312,9 +311,9 @@ _moved_attributes = [
MovedModule("tkinter_messagebox", "tkMessageBox", "tkinter.messagebox"), MovedModule("tkinter_messagebox", "tkMessageBox", "tkinter.messagebox"),
MovedModule("tkinter_tksimpledialog", "tkSimpleDialog", MovedModule("tkinter_tksimpledialog", "tkSimpleDialog",
"tkinter.simpledialog"), "tkinter.simpledialog"),
MovedModule("urllib_parse", __name__ + ".moves.urllib_parse", "urllib.parse"), MovedModule("urllib_parse", f"{__name__}.moves.urllib_parse", "urllib.parse"),
MovedModule("urllib_error", __name__ + ".moves.urllib_error", "urllib.error"), MovedModule("urllib_error", f"{__name__}.moves.urllib_error", "urllib.error"),
MovedModule("urllib", __name__ + ".moves.urllib", __name__ + ".moves.urllib"), MovedModule("urllib", f"{__name__}.moves.urllib", f"{__name__}.moves.urllib"),
MovedModule("urllib_robotparser", "robotparser", "urllib.robotparser"), MovedModule("urllib_robotparser", "robotparser", "urllib.robotparser"),
MovedModule("xmlrpc_client", "xmlrpclib", "xmlrpc.client"), MovedModule("xmlrpc_client", "xmlrpclib", "xmlrpc.client"),
MovedModule("xmlrpc_server", "SimpleXMLRPCServer", "xmlrpc.server"), MovedModule("xmlrpc_server", "SimpleXMLRPCServer", "xmlrpc.server"),
@ -328,12 +327,12 @@ if sys.platform == "win32":
for attr in _moved_attributes: for attr in _moved_attributes:
setattr(_MovedItems, attr.name, attr) setattr(_MovedItems, attr.name, attr)
if isinstance(attr, MovedModule): if isinstance(attr, MovedModule):
_importer._add_module(attr, "moves." + attr.name) _importer._add_module(attr, f"moves.{attr.name}")
del attr del attr
_MovedItems._moved_attributes = _moved_attributes _MovedItems._moved_attributes = _moved_attributes
moves = _MovedItems(__name__ + ".moves") moves = _MovedItems(f"{__name__}.moves")
_importer._add_module(moves, "moves") _importer._add_module(moves, "moves")
@ -375,7 +374,7 @@ del attr
Module_six_moves_urllib_parse._moved_attributes = _urllib_parse_moved_attributes Module_six_moves_urllib_parse._moved_attributes = _urllib_parse_moved_attributes
_importer._add_module(Module_six_moves_urllib_parse(__name__ + ".moves.urllib_parse"), _importer._add_module(Module_six_moves_urllib_parse(f"{__name__}.moves.urllib_parse"),
"moves.urllib_parse", "moves.urllib.parse") "moves.urllib_parse", "moves.urllib.parse")
@ -395,7 +394,7 @@ del attr
Module_six_moves_urllib_error._moved_attributes = _urllib_error_moved_attributes Module_six_moves_urllib_error._moved_attributes = _urllib_error_moved_attributes
_importer._add_module(Module_six_moves_urllib_error(__name__ + ".moves.urllib.error"), _importer._add_module(Module_six_moves_urllib_error(f"{__name__}.moves.urllib.error"),
"moves.urllib_error", "moves.urllib.error") "moves.urllib_error", "moves.urllib.error")
@ -447,7 +446,7 @@ del attr
Module_six_moves_urllib_request._moved_attributes = _urllib_request_moved_attributes Module_six_moves_urllib_request._moved_attributes = _urllib_request_moved_attributes
_importer._add_module(Module_six_moves_urllib_request(__name__ + ".moves.urllib.request"), _importer._add_module(Module_six_moves_urllib_request(f"{__name__}.moves.urllib.request"),
"moves.urllib_request", "moves.urllib.request") "moves.urllib_request", "moves.urllib.request")
@ -468,7 +467,7 @@ del attr
Module_six_moves_urllib_response._moved_attributes = _urllib_response_moved_attributes Module_six_moves_urllib_response._moved_attributes = _urllib_response_moved_attributes
_importer._add_module(Module_six_moves_urllib_response(__name__ + ".moves.urllib.response"), _importer._add_module(Module_six_moves_urllib_response(f"{__name__}.moves.urllib.response"),
"moves.urllib_response", "moves.urllib.response") "moves.urllib_response", "moves.urllib.response")
@ -486,7 +485,7 @@ del attr
Module_six_moves_urllib_robotparser._moved_attributes = _urllib_robotparser_moved_attributes Module_six_moves_urllib_robotparser._moved_attributes = _urllib_robotparser_moved_attributes
_importer._add_module(Module_six_moves_urllib_robotparser(__name__ + ".moves.urllib.robotparser"), _importer._add_module(Module_six_moves_urllib_robotparser(f"{__name__}.moves.urllib.robotparser"),
"moves.urllib_robotparser", "moves.urllib.robotparser") "moves.urllib_robotparser", "moves.urllib.robotparser")
@ -503,7 +502,7 @@ class Module_six_moves_urllib(types.ModuleType):
def __dir__(self): def __dir__(self):
return ['parse', 'error', 'request', 'response', 'robotparser'] return ['parse', 'error', 'request', 'response', 'robotparser']
_importer._add_module(Module_six_moves_urllib(__name__ + ".moves.urllib"), _importer._add_module(Module_six_moves_urllib(f"{__name__}.moves.urllib"),
"moves.urllib") "moves.urllib")
@ -520,7 +519,7 @@ def remove_move(name):
try: try:
del moves.__dict__[name] del moves.__dict__[name]
except KeyError: except KeyError:
raise AttributeError("no such move, %r" % (name,)) raise AttributeError(f"no such move, {name!r}")
if PY3: if PY3:
@ -576,7 +575,7 @@ else:
def create_unbound_method(func, cls): def create_unbound_method(func, cls):
return types.MethodType(func, None, cls) return types.MethodType(func, None, cls)
class Iterator(object): class Iterator:
def next(self): def next(self):
return type(self).__next__(self) return type(self).__next__(self)
@ -910,7 +909,7 @@ def ensure_binary(s, encoding='utf-8', errors='strict'):
return s return s
if isinstance(s, text_type): if isinstance(s, text_type):
return s.encode(encoding, errors) return s.encode(encoding, errors)
raise TypeError("not expecting type '%s'" % type(s)) raise TypeError(f"not expecting type '{type(s)}'")
def ensure_str(s, encoding='utf-8', errors='strict'): def ensure_str(s, encoding='utf-8', errors='strict'):
@ -932,7 +931,7 @@ def ensure_str(s, encoding='utf-8', errors='strict'):
elif PY3 and isinstance(s, binary_type): elif PY3 and isinstance(s, binary_type):
return s.decode(encoding, errors) return s.decode(encoding, errors)
elif not isinstance(s, (text_type, binary_type)): elif not isinstance(s, (text_type, binary_type)):
raise TypeError("not expecting type '%s'" % type(s)) raise TypeError(f"not expecting type '{type(s)}'")
return s return s
@ -952,7 +951,7 @@ def ensure_text(s, encoding='utf-8', errors='strict'):
elif isinstance(s, text_type): elif isinstance(s, text_type):
return s return s
else: else:
raise TypeError("not expecting type '%s'" % type(s)) raise TypeError(f"not expecting type '{type(s)}'")
def python_2_unicode_compatible(klass): def python_2_unicode_compatible(klass):
@ -965,9 +964,7 @@ def python_2_unicode_compatible(klass):
""" """
if PY2: if PY2:
if '__str__' not in klass.__dict__: if '__str__' not in klass.__dict__:
raise ValueError("@python_2_unicode_compatible cannot be applied " raise ValueError(f"@python_2_unicode_compatible cannot be applied to {klass.__name__} because it doesn't define __str__().")
"to %s because it doesn't define __str__()." %
klass.__name__)
klass.__unicode__ = klass.__str__ klass.__unicode__ = klass.__str__
klass.__str__ = lambda self: self.__unicode__().encode('utf-8') klass.__str__ = lambda self: self.__unicode__().encode('utf-8')
return klass return klass

View File

@ -52,7 +52,7 @@ import io
if sys.version_info[0:2] >= (3, 3): if sys.version_info[0:2] >= (3, 3):
from collections.abc import Hashable from collections.abc import Hashable
else: else:
from collections import Hashable from collections.abc import Hashable
__version__ = "2.7.1" __version__ = "2.7.1"
"Module version string" "Module version string"
@ -66,7 +66,7 @@ version = (2, 7, 1)
############################################################################## ##############################################################################
# Extension type for application-defined types and data # Extension type for application-defined types and data
class Ext(object): class Ext:
""" """
The Ext class facilitates creating a serializable extension object to store The Ext class facilitates creating a serializable extension object to store
an application-defined type and data byte array. an application-defined type and data byte array.
@ -100,7 +100,7 @@ class Ext(object):
if not isinstance(type, int): if not isinstance(type, int):
raise TypeError("ext type is not type integer") raise TypeError("ext type is not type integer")
elif not (-2**7 <= type <= 2**7 - 1): elif not (-2**7 <= type <= 2**7 - 1):
raise ValueError("ext type value {:d} is out of range (-128 to 127)".format(type)) raise ValueError(f"ext type value {type} is out of range (-128 to 127)")
# Check data is type bytes or str # Check data is type bytes or str
elif sys.version_info[0] == 3 and not isinstance(data, bytes): elif sys.version_info[0] == 3 and not isinstance(data, bytes):
raise TypeError("ext data is not type \'bytes\'") raise TypeError("ext data is not type \'bytes\'")
@ -127,8 +127,8 @@ class Ext(object):
""" """
String representation of this Ext object. String representation of this Ext object.
""" """
s = "Ext Object (Type: {:d}, Data: ".format(self.type) s = f"Ext Object (Type: {self.type}, Data: "
s += " ".join(["0x{:02}".format(ord(self.data[i:i + 1])) s += " ".join([f"0x{ord(self.data[i:i + 1]):02}"
for i in xrange(min(len(self.data), 8))]) for i in xrange(min(len(self.data), 8))])
if len(self.data) > 8: if len(self.data) > 8:
s += " ..." s += " ..."
@ -177,11 +177,11 @@ def ext_serializable(ext_type):
if not isinstance(ext_type, int): if not isinstance(ext_type, int):
raise TypeError("Ext type is not type integer") raise TypeError("Ext type is not type integer")
elif not (-2**7 <= ext_type <= 2**7 - 1): elif not (-2**7 <= ext_type <= 2**7 - 1):
raise ValueError("Ext type value {:d} is out of range of -128 to 127".format(ext_type)) raise ValueError(f"Ext type value {ext_type} is out of range of -128 to 127")
elif ext_type in _ext_type_to_class: elif ext_type in _ext_type_to_class:
raise ValueError("Ext type {:d} already registered with class {:s}".format(ext_type, repr(_ext_type_to_class[ext_type]))) raise ValueError(f"Ext type {ext_type} already registered with class {_ext_type_to_class[ext_type]!r}")
elif cls in _ext_class_to_type: elif cls in _ext_class_to_type:
raise ValueError("Class {:s} already registered with Ext type {:d}".format(repr(cls), ext_type)) raise ValueError(f"Class {cls!r} already registered with Ext type {ext_type}")
_ext_type_to_class[ext_type] = cls _ext_type_to_class[ext_type] = cls
_ext_class_to_type[cls] = ext_type _ext_class_to_type[cls] = ext_type
@ -495,7 +495,7 @@ def _pack2(obj, fp, **options):
try: try:
_pack_ext(Ext(_ext_class_to_type[obj.__class__], obj.packb()), fp, options) _pack_ext(Ext(_ext_class_to_type[obj.__class__], obj.packb()), fp, options)
except AttributeError: except AttributeError:
raise NotImplementedError("Ext serializable class {:s} is missing implementation of packb()".format(repr(obj.__class__))) raise NotImplementedError(f"Ext serializable class {obj.__class__!r} is missing implementation of packb()")
elif isinstance(obj, bool): elif isinstance(obj, bool):
_pack_boolean(obj, fp, options) _pack_boolean(obj, fp, options)
elif isinstance(obj, (int, long)): elif isinstance(obj, (int, long)):
@ -525,7 +525,7 @@ def _pack2(obj, fp, **options):
_pack_ext(ext_handlers[t](obj), fp, options) _pack_ext(ext_handlers[t](obj), fp, options)
else: else:
raise UnsupportedTypeException( raise UnsupportedTypeException(
"unsupported type: {:s}".format(str(type(obj)))) f"unsupported type: {type(obj)}")
elif _ext_class_to_type: elif _ext_class_to_type:
# Linear search for superclass # Linear search for superclass
t = next((t for t in _ext_class_to_type if isinstance(obj, t)), None) t = next((t for t in _ext_class_to_type if isinstance(obj, t)), None)
@ -533,11 +533,11 @@ def _pack2(obj, fp, **options):
try: try:
_pack_ext(Ext(_ext_class_to_type[t], obj.packb()), fp, options) _pack_ext(Ext(_ext_class_to_type[t], obj.packb()), fp, options)
except AttributeError: except AttributeError:
raise NotImplementedError("Ext serializable class {:s} is missing implementation of packb()".format(repr(t))) raise NotImplementedError(f"Ext serializable class {t!r} is missing implementation of packb()")
else: else:
raise UnsupportedTypeException("unsupported type: {:s}".format(str(type(obj)))) raise UnsupportedTypeException(f"unsupported type: {type(obj)}")
else: else:
raise UnsupportedTypeException("unsupported type: {:s}".format(str(type(obj)))) raise UnsupportedTypeException(f"unsupported type: {type(obj)}")
# Pack for Python 3, with unicode 'str' type, 'bytes' type, and no 'long' type # Pack for Python 3, with unicode 'str' type, 'bytes' type, and no 'long' type
@ -582,7 +582,7 @@ def _pack3(obj, fp, **options):
try: try:
_pack_ext(Ext(_ext_class_to_type[obj.__class__], obj.packb()), fp, options) _pack_ext(Ext(_ext_class_to_type[obj.__class__], obj.packb()), fp, options)
except AttributeError: except AttributeError:
raise NotImplementedError("Ext serializable class {:s} is missing implementation of packb()".format(repr(obj.__class__))) raise NotImplementedError(f"Ext serializable class {obj.__class__!r} is missing implementation of packb()")
elif isinstance(obj, bool): elif isinstance(obj, bool):
_pack_boolean(obj, fp, options) _pack_boolean(obj, fp, options)
elif isinstance(obj, int): elif isinstance(obj, int):
@ -612,7 +612,7 @@ def _pack3(obj, fp, **options):
_pack_ext(ext_handlers[t](obj), fp, options) _pack_ext(ext_handlers[t](obj), fp, options)
else: else:
raise UnsupportedTypeException( raise UnsupportedTypeException(
"unsupported type: {:s}".format(str(type(obj)))) f"unsupported type: {type(obj)}")
elif _ext_class_to_type: elif _ext_class_to_type:
# Linear search for superclass # Linear search for superclass
t = next((t for t in _ext_class_to_type if isinstance(obj, t)), None) t = next((t for t in _ext_class_to_type if isinstance(obj, t)), None)
@ -620,12 +620,12 @@ def _pack3(obj, fp, **options):
try: try:
_pack_ext(Ext(_ext_class_to_type[t], obj.packb()), fp, options) _pack_ext(Ext(_ext_class_to_type[t], obj.packb()), fp, options)
except AttributeError: except AttributeError:
raise NotImplementedError("Ext serializable class {:s} is missing implementation of packb()".format(repr(t))) raise NotImplementedError(f"Ext serializable class {t!r} is missing implementation of packb()")
else: else:
raise UnsupportedTypeException("unsupported type: {:s}".format(str(type(obj)))) raise UnsupportedTypeException(f"unsupported type: {type(obj)}")
else: else:
raise UnsupportedTypeException( raise UnsupportedTypeException(
"unsupported type: {:s}".format(str(type(obj)))) f"unsupported type: {type(obj)}")
def _packb2(obj, **options): def _packb2(obj, **options):
@ -737,21 +737,21 @@ def _unpack_integer(code, fp, options):
return struct.unpack(">I", _read_except(fp, 4))[0] return struct.unpack(">I", _read_except(fp, 4))[0]
elif code == b'\xcf': elif code == b'\xcf':
return struct.unpack(">Q", _read_except(fp, 8))[0] return struct.unpack(">Q", _read_except(fp, 8))[0]
raise Exception("logic error, not int: 0x{:02x}".format(ord(code))) raise Exception(f"logic error, not int: 0x{ord(code):02x}")
def _unpack_reserved(code, fp, options): def _unpack_reserved(code, fp, options):
if code == b'\xc1': if code == b'\xc1':
raise ReservedCodeException( raise ReservedCodeException(
"encountered reserved code: 0x{:02x}".format(ord(code))) f"encountered reserved code: 0x{ord(code):02x}")
raise Exception( raise Exception(
"logic error, not reserved code: 0x{:02x}".format(ord(code))) f"logic error, not reserved code: 0x{ord(code):02x}")
def _unpack_nil(code, fp, options): def _unpack_nil(code, fp, options):
if code == b'\xc0': if code == b'\xc0':
return None return None
raise Exception("logic error, not nil: 0x{:02x}".format(ord(code))) raise Exception(f"logic error, not nil: 0x{ord(code):02x}")
def _unpack_boolean(code, fp, options): def _unpack_boolean(code, fp, options):
@ -759,7 +759,7 @@ def _unpack_boolean(code, fp, options):
return False return False
elif code == b'\xc3': elif code == b'\xc3':
return True return True
raise Exception("logic error, not boolean: 0x{:02x}".format(ord(code))) raise Exception(f"logic error, not boolean: 0x{ord(code):02x}")
def _unpack_float(code, fp, options): def _unpack_float(code, fp, options):
@ -767,7 +767,7 @@ def _unpack_float(code, fp, options):
return struct.unpack(">f", _read_except(fp, 4))[0] return struct.unpack(">f", _read_except(fp, 4))[0]
elif code == b'\xcb': elif code == b'\xcb':
return struct.unpack(">d", _read_except(fp, 8))[0] return struct.unpack(">d", _read_except(fp, 8))[0]
raise Exception("logic error, not float: 0x{:02x}".format(ord(code))) raise Exception(f"logic error, not float: 0x{ord(code):02x}")
def _unpack_string(code, fp, options): def _unpack_string(code, fp, options):
@ -780,7 +780,7 @@ def _unpack_string(code, fp, options):
elif code == b'\xdb': elif code == b'\xdb':
length = struct.unpack(">I", _read_except(fp, 4))[0] length = struct.unpack(">I", _read_except(fp, 4))[0]
else: else:
raise Exception("logic error, not string: 0x{:02x}".format(ord(code))) raise Exception(f"logic error, not string: 0x{ord(code):02x}")
# Always return raw bytes in compatibility mode # Always return raw bytes in compatibility mode
global compatibility global compatibility
@ -804,7 +804,7 @@ def _unpack_binary(code, fp, options):
elif code == b'\xc6': elif code == b'\xc6':
length = struct.unpack(">I", _read_except(fp, 4))[0] length = struct.unpack(">I", _read_except(fp, 4))[0]
else: else:
raise Exception("logic error, not binary: 0x{:02x}".format(ord(code))) raise Exception(f"logic error, not binary: 0x{ord(code):02x}")
return _read_except(fp, length) return _read_except(fp, length)
@ -827,7 +827,7 @@ def _unpack_ext(code, fp, options):
elif code == b'\xc9': elif code == b'\xc9':
length = struct.unpack(">I", _read_except(fp, 4))[0] length = struct.unpack(">I", _read_except(fp, 4))[0]
else: else:
raise Exception("logic error, not ext: 0x{:02x}".format(ord(code))) raise Exception(f"logic error, not ext: 0x{ord(code):02x}")
ext_type = struct.unpack("b", _read_except(fp, 1))[0] ext_type = struct.unpack("b", _read_except(fp, 1))[0]
ext_data = _read_except(fp, length) ext_data = _read_except(fp, length)
@ -842,7 +842,7 @@ def _unpack_ext(code, fp, options):
try: try:
return _ext_type_to_class[ext_type].unpackb(ext_data) return _ext_type_to_class[ext_type].unpackb(ext_data)
except AttributeError: except AttributeError:
raise NotImplementedError("Ext serializable class {:s} is missing implementation of unpackb()".format(repr(_ext_type_to_class[ext_type]))) raise NotImplementedError(f"Ext serializable class {_ext_type_to_class[ext_type]!r} is missing implementation of unpackb()")
# Timestamp extension # Timestamp extension
if ext_type == -1: if ext_type == -1:
@ -868,7 +868,7 @@ def _unpack_ext_timestamp(ext_data, options):
microseconds = struct.unpack(">I", ext_data[0:4])[0] // 1000 microseconds = struct.unpack(">I", ext_data[0:4])[0] // 1000
else: else:
raise UnsupportedTimestampException( raise UnsupportedTimestampException(
"unsupported timestamp with data length {:d}".format(len(ext_data))) f"unsupported timestamp with data length {len(ext_data)}")
return _epoch + datetime.timedelta(seconds=seconds, return _epoch + datetime.timedelta(seconds=seconds,
microseconds=microseconds) microseconds=microseconds)
@ -882,10 +882,10 @@ def _unpack_array(code, fp, options):
elif code == b'\xdd': elif code == b'\xdd':
length = struct.unpack(">I", _read_except(fp, 4))[0] length = struct.unpack(">I", _read_except(fp, 4))[0]
else: else:
raise Exception("logic error, not array: 0x{:02x}".format(ord(code))) raise Exception(f"logic error, not array: 0x{ord(code):02x}")
if options.get('use_tuple'): if options.get('use_tuple'):
return tuple((_unpack(fp, options) for i in xrange(length))) return tuple(_unpack(fp, options) for i in xrange(length))
return [_unpack(fp, options) for i in xrange(length)] return [_unpack(fp, options) for i in xrange(length)]
@ -904,7 +904,7 @@ def _unpack_map(code, fp, options):
elif code == b'\xdf': elif code == b'\xdf':
length = struct.unpack(">I", _read_except(fp, 4))[0] length = struct.unpack(">I", _read_except(fp, 4))[0]
else: else:
raise Exception("logic error, not map: 0x{:02x}".format(ord(code))) raise Exception(f"logic error, not map: 0x{ord(code):02x}")
d = {} if not options.get('use_ordered_dict') else collections.OrderedDict() d = {} if not options.get('use_ordered_dict') else collections.OrderedDict()
for _ in xrange(length): for _ in xrange(length):
@ -916,10 +916,10 @@ def _unpack_map(code, fp, options):
k = _deep_list_to_tuple(k) k = _deep_list_to_tuple(k)
elif not isinstance(k, Hashable): elif not isinstance(k, Hashable):
raise UnhashableKeyException( raise UnhashableKeyException(
"encountered unhashable key: \"{:s}\" ({:s})".format(str(k), str(type(k)))) f"encountered unhashable key: \"{k}\" ({type(k)})")
elif k in d: elif k in d:
raise DuplicateKeyException( raise DuplicateKeyException(
"encountered duplicate key: \"{:s}\" ({:s})".format(str(k), str(type(k)))) f"encountered duplicate key: \"{k}\" ({type(k)})")
# Unpack value # Unpack value
v = _unpack(fp, options) v = _unpack(fp, options)
@ -928,7 +928,7 @@ def _unpack_map(code, fp, options):
d[k] = v d[k] = v
except TypeError: except TypeError:
raise UnhashableKeyException( raise UnhashableKeyException(
"encountered unhashable key: \"{:s}\"".format(str(k))) f"encountered unhashable key: \"{k}\"")
return d return d

Binary file not shown.

Binary file not shown.

View File

@ -1,4 +1,4 @@
# Sphinx build info version 1 # Sphinx build info version 1
# This file hashes the configuration used when building these files. When it is not found, a full rebuild will be done. # This file hashes the configuration used when building these files. When it is not found, a full rebuild will be done.
config: bd0a676b0f490cda5d841ab7cd7cade1 config: ab3a817981de13f74ede3061942f75d2
tags: 645f666f9bcd5a90fca523b33c5a78b7 tags: 645f666f9bcd5a90fca523b33c5a78b7

View File

@ -1,6 +1,6 @@
var DOCUMENTATION_OPTIONS = { var DOCUMENTATION_OPTIONS = {
URL_ROOT: document.getElementById("documentation_options").getAttribute('data-url_root'), URL_ROOT: document.getElementById("documentation_options").getAttribute('data-url_root'),
VERSION: '0.8.2 beta', VERSION: '0.8.4 beta',
LANGUAGE: 'en', LANGUAGE: 'en',
COLLAPSE_INDEX: false, COLLAPSE_INDEX: false,
BUILDER: 'html', BUILDER: 'html',

View File

@ -6,7 +6,7 @@
<link rel="index" title="Index" href="genindex.html" /><link rel="search" title="Search" href="search.html" /><link rel="next" title="Support Reticulum" href="support.html" /><link rel="prev" title="Building Networks" href="networks.html" /> <link rel="index" title="Index" href="genindex.html" /><link rel="search" title="Search" href="search.html" /><link rel="next" title="Support Reticulum" href="support.html" /><link rel="prev" title="Building Networks" href="networks.html" />
<meta name="generator" content="sphinx-5.3.0, furo 2022.09.29.dev1"/> <meta name="generator" content="sphinx-5.3.0, furo 2022.09.29.dev1"/>
<title>Code Examples - Reticulum Network Stack 0.8.2 beta documentation</title> <title>Code Examples - Reticulum Network Stack 0.8.4 beta documentation</title>
<link rel="stylesheet" type="text/css" href="_static/pygments.css" /> <link rel="stylesheet" type="text/css" href="_static/pygments.css" />
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=189ec851f9bb375a2509b67be1f64f0cf212b702" /> <link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=189ec851f9bb375a2509b67be1f64f0cf212b702" />
<link rel="stylesheet" type="text/css" href="_static/copybutton.css" /> <link rel="stylesheet" type="text/css" href="_static/copybutton.css" />
@ -141,7 +141,7 @@
</label> </label>
</div> </div>
<div class="header-center"> <div class="header-center">
<a href="index.html"><div class="brand">Reticulum Network Stack 0.8.2 beta documentation</div></a> <a href="index.html"><div class="brand">Reticulum Network Stack 0.8.4 beta documentation</div></a>
</div> </div>
<div class="header-right"> <div class="header-right">
<div class="theme-toggle-container theme-toggle-header"> <div class="theme-toggle-container theme-toggle-header">
@ -167,7 +167,7 @@
<img class="sidebar-logo" src="_static/rns_logo_512.png" alt="Logo"/> <img class="sidebar-logo" src="_static/rns_logo_512.png" alt="Logo"/>
</div> </div>
<span class="sidebar-brand-text">Reticulum Network Stack 0.8.2 beta documentation</span> <span class="sidebar-brand-text">Reticulum Network Stack 0.8.4 beta documentation</span>
</a><form class="sidebar-search-container" method="get" action="search.html" role="search"> </a><form class="sidebar-search-container" method="get" action="search.html" role="search">
<input class="sidebar-search" placeholder="Search" name="q" aria-label="Search"> <input class="sidebar-search" placeholder="Search" name="q" aria-label="Search">

View File

@ -6,7 +6,7 @@
<link rel="index" title="Index" href="genindex.html" /><link rel="search" title="Search" href="search.html" /> <link rel="index" title="Index" href="genindex.html" /><link rel="search" title="Search" href="search.html" />
<meta name="generator" content="sphinx-5.3.0, furo 2022.09.29.dev1"/> <meta name="generator" content="sphinx-5.3.0, furo 2022.09.29.dev1"/>
<title>An Explanation of Reticulum for Human Beings - Reticulum Network Stack 0.8.2 beta documentation</title> <title>An Explanation of Reticulum for Human Beings - Reticulum Network Stack 0.8.4 beta documentation</title>
<link rel="stylesheet" type="text/css" href="_static/pygments.css" /> <link rel="stylesheet" type="text/css" href="_static/pygments.css" />
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=189ec851f9bb375a2509b67be1f64f0cf212b702" /> <link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=189ec851f9bb375a2509b67be1f64f0cf212b702" />
<link rel="stylesheet" type="text/css" href="_static/copybutton.css" /> <link rel="stylesheet" type="text/css" href="_static/copybutton.css" />
@ -141,7 +141,7 @@
</label> </label>
</div> </div>
<div class="header-center"> <div class="header-center">
<a href="index.html"><div class="brand">Reticulum Network Stack 0.8.2 beta documentation</div></a> <a href="index.html"><div class="brand">Reticulum Network Stack 0.8.4 beta documentation</div></a>
</div> </div>
<div class="header-right"> <div class="header-right">
<div class="theme-toggle-container theme-toggle-header"> <div class="theme-toggle-container theme-toggle-header">
@ -167,7 +167,7 @@
<img class="sidebar-logo" src="_static/rns_logo_512.png" alt="Logo"/> <img class="sidebar-logo" src="_static/rns_logo_512.png" alt="Logo"/>
</div> </div>
<span class="sidebar-brand-text">Reticulum Network Stack 0.8.2 beta documentation</span> <span class="sidebar-brand-text">Reticulum Network Stack 0.8.4 beta documentation</span>
</a><form class="sidebar-search-container" method="get" action="search.html" role="search"> </a><form class="sidebar-search-container" method="get" action="search.html" role="search">
<input class="sidebar-search" placeholder="Search" name="q" aria-label="Search"> <input class="sidebar-search" placeholder="Search" name="q" aria-label="Search">

View File

@ -4,7 +4,7 @@
<meta name="viewport" content="width=device-width,initial-scale=1"/> <meta name="viewport" content="width=device-width,initial-scale=1"/>
<meta name="color-scheme" content="light dark"><link rel="index" title="Index" href="#" /><link rel="search" title="Search" href="search.html" /> <meta name="color-scheme" content="light dark"><link rel="index" title="Index" href="#" /><link rel="search" title="Search" href="search.html" />
<meta name="generator" content="sphinx-5.3.0, furo 2022.09.29.dev1"/><title>Index - Reticulum Network Stack 0.8.2 beta documentation</title> <meta name="generator" content="sphinx-5.3.0, furo 2022.09.29.dev1"/><title>Index - Reticulum Network Stack 0.8.4 beta documentation</title>
<link rel="stylesheet" type="text/css" href="_static/pygments.css" /> <link rel="stylesheet" type="text/css" href="_static/pygments.css" />
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=189ec851f9bb375a2509b67be1f64f0cf212b702" /> <link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=189ec851f9bb375a2509b67be1f64f0cf212b702" />
<link rel="stylesheet" type="text/css" href="_static/copybutton.css" /> <link rel="stylesheet" type="text/css" href="_static/copybutton.css" />
@ -139,7 +139,7 @@
</label> </label>
</div> </div>
<div class="header-center"> <div class="header-center">
<a href="index.html"><div class="brand">Reticulum Network Stack 0.8.2 beta documentation</div></a> <a href="index.html"><div class="brand">Reticulum Network Stack 0.8.4 beta documentation</div></a>
</div> </div>
<div class="header-right"> <div class="header-right">
<div class="theme-toggle-container theme-toggle-header"> <div class="theme-toggle-container theme-toggle-header">
@ -165,7 +165,7 @@
<img class="sidebar-logo" src="_static/rns_logo_512.png" alt="Logo"/> <img class="sidebar-logo" src="_static/rns_logo_512.png" alt="Logo"/>
</div> </div>
<span class="sidebar-brand-text">Reticulum Network Stack 0.8.2 beta documentation</span> <span class="sidebar-brand-text">Reticulum Network Stack 0.8.4 beta documentation</span>
</a><form class="sidebar-search-container" method="get" action="search.html" role="search"> </a><form class="sidebar-search-container" method="get" action="search.html" role="search">
<input class="sidebar-search" placeholder="Search" name="q" aria-label="Search"> <input class="sidebar-search" placeholder="Search" name="q" aria-label="Search">

View File

@ -6,7 +6,7 @@
<link rel="index" title="Index" href="genindex.html" /><link rel="search" title="Search" href="search.html" /><link rel="next" title="Using Reticulum on Your System" href="using.html" /><link rel="prev" title="What is Reticulum?" href="whatis.html" /> <link rel="index" title="Index" href="genindex.html" /><link rel="search" title="Search" href="search.html" /><link rel="next" title="Using Reticulum on Your System" href="using.html" /><link rel="prev" title="What is Reticulum?" href="whatis.html" />
<meta name="generator" content="sphinx-5.3.0, furo 2022.09.29.dev1"/> <meta name="generator" content="sphinx-5.3.0, furo 2022.09.29.dev1"/>
<title>Getting Started Fast - Reticulum Network Stack 0.8.2 beta documentation</title> <title>Getting Started Fast - Reticulum Network Stack 0.8.4 beta documentation</title>
<link rel="stylesheet" type="text/css" href="_static/pygments.css" /> <link rel="stylesheet" type="text/css" href="_static/pygments.css" />
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=189ec851f9bb375a2509b67be1f64f0cf212b702" /> <link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=189ec851f9bb375a2509b67be1f64f0cf212b702" />
<link rel="stylesheet" type="text/css" href="_static/copybutton.css" /> <link rel="stylesheet" type="text/css" href="_static/copybutton.css" />
@ -141,7 +141,7 @@
</label> </label>
</div> </div>
<div class="header-center"> <div class="header-center">
<a href="index.html"><div class="brand">Reticulum Network Stack 0.8.2 beta documentation</div></a> <a href="index.html"><div class="brand">Reticulum Network Stack 0.8.4 beta documentation</div></a>
</div> </div>
<div class="header-right"> <div class="header-right">
<div class="theme-toggle-container theme-toggle-header"> <div class="theme-toggle-container theme-toggle-header">
@ -167,7 +167,7 @@
<img class="sidebar-logo" src="_static/rns_logo_512.png" alt="Logo"/> <img class="sidebar-logo" src="_static/rns_logo_512.png" alt="Logo"/>
</div> </div>
<span class="sidebar-brand-text">Reticulum Network Stack 0.8.2 beta documentation</span> <span class="sidebar-brand-text">Reticulum Network Stack 0.8.4 beta documentation</span>
</a><form class="sidebar-search-container" method="get" action="search.html" role="search"> </a><form class="sidebar-search-container" method="get" action="search.html" role="search">
<input class="sidebar-search" placeholder="Search" name="q" aria-label="Search"> <input class="sidebar-search" placeholder="Search" name="q" aria-label="Search">

View File

@ -6,7 +6,7 @@
<link rel="index" title="Index" href="genindex.html" /><link rel="search" title="Search" href="search.html" /><link rel="next" title="Configuring Interfaces" href="interfaces.html" /><link rel="prev" title="Understanding Reticulum" href="understanding.html" /> <link rel="index" title="Index" href="genindex.html" /><link rel="search" title="Search" href="search.html" /><link rel="next" title="Configuring Interfaces" href="interfaces.html" /><link rel="prev" title="Understanding Reticulum" href="understanding.html" />
<meta name="generator" content="sphinx-5.3.0, furo 2022.09.29.dev1"/> <meta name="generator" content="sphinx-5.3.0, furo 2022.09.29.dev1"/>
<title>Communications Hardware - Reticulum Network Stack 0.8.2 beta documentation</title> <title>Communications Hardware - Reticulum Network Stack 0.8.4 beta documentation</title>
<link rel="stylesheet" type="text/css" href="_static/pygments.css" /> <link rel="stylesheet" type="text/css" href="_static/pygments.css" />
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=189ec851f9bb375a2509b67be1f64f0cf212b702" /> <link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=189ec851f9bb375a2509b67be1f64f0cf212b702" />
<link rel="stylesheet" type="text/css" href="_static/copybutton.css" /> <link rel="stylesheet" type="text/css" href="_static/copybutton.css" />
@ -141,7 +141,7 @@
</label> </label>
</div> </div>
<div class="header-center"> <div class="header-center">
<a href="index.html"><div class="brand">Reticulum Network Stack 0.8.2 beta documentation</div></a> <a href="index.html"><div class="brand">Reticulum Network Stack 0.8.4 beta documentation</div></a>
</div> </div>
<div class="header-right"> <div class="header-right">
<div class="theme-toggle-container theme-toggle-header"> <div class="theme-toggle-container theme-toggle-header">
@ -167,7 +167,7 @@
<img class="sidebar-logo" src="_static/rns_logo_512.png" alt="Logo"/> <img class="sidebar-logo" src="_static/rns_logo_512.png" alt="Logo"/>
</div> </div>
<span class="sidebar-brand-text">Reticulum Network Stack 0.8.2 beta documentation</span> <span class="sidebar-brand-text">Reticulum Network Stack 0.8.4 beta documentation</span>
</a><form class="sidebar-search-container" method="get" action="search.html" role="search"> </a><form class="sidebar-search-container" method="get" action="search.html" role="search">
<input class="sidebar-search" placeholder="Search" name="q" aria-label="Search"> <input class="sidebar-search" placeholder="Search" name="q" aria-label="Search">

View File

@ -6,7 +6,7 @@
<link rel="index" title="Index" href="genindex.html" /><link rel="search" title="Search" href="search.html" /><link rel="next" title="What is Reticulum?" href="whatis.html" /> <link rel="index" title="Index" href="genindex.html" /><link rel="search" title="Search" href="search.html" /><link rel="next" title="What is Reticulum?" href="whatis.html" />
<meta name="generator" content="sphinx-5.3.0, furo 2022.09.29.dev1"/> <meta name="generator" content="sphinx-5.3.0, furo 2022.09.29.dev1"/>
<title>Reticulum Network Stack 0.8.2 beta documentation</title> <title>Reticulum Network Stack 0.8.4 beta documentation</title>
<link rel="stylesheet" type="text/css" href="_static/pygments.css" /> <link rel="stylesheet" type="text/css" href="_static/pygments.css" />
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=189ec851f9bb375a2509b67be1f64f0cf212b702" /> <link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=189ec851f9bb375a2509b67be1f64f0cf212b702" />
<link rel="stylesheet" type="text/css" href="_static/copybutton.css" /> <link rel="stylesheet" type="text/css" href="_static/copybutton.css" />
@ -141,7 +141,7 @@
</label> </label>
</div> </div>
<div class="header-center"> <div class="header-center">
<a href="#"><div class="brand">Reticulum Network Stack 0.8.2 beta documentation</div></a> <a href="#"><div class="brand">Reticulum Network Stack 0.8.4 beta documentation</div></a>
</div> </div>
<div class="header-right"> <div class="header-right">
<div class="theme-toggle-container theme-toggle-header"> <div class="theme-toggle-container theme-toggle-header">
@ -167,7 +167,7 @@
<img class="sidebar-logo" src="_static/rns_logo_512.png" alt="Logo"/> <img class="sidebar-logo" src="_static/rns_logo_512.png" alt="Logo"/>
</div> </div>
<span class="sidebar-brand-text">Reticulum Network Stack 0.8.2 beta documentation</span> <span class="sidebar-brand-text">Reticulum Network Stack 0.8.4 beta documentation</span>
</a><form class="sidebar-search-container" method="get" action="search.html" role="search"> </a><form class="sidebar-search-container" method="get" action="search.html" role="search">
<input class="sidebar-search" placeholder="Search" name="q" aria-label="Search"> <input class="sidebar-search" placeholder="Search" name="q" aria-label="Search">

View File

@ -6,7 +6,7 @@
<link rel="index" title="Index" href="genindex.html" /><link rel="search" title="Search" href="search.html" /><link rel="next" title="Building Networks" href="networks.html" /><link rel="prev" title="Communications Hardware" href="hardware.html" /> <link rel="index" title="Index" href="genindex.html" /><link rel="search" title="Search" href="search.html" /><link rel="next" title="Building Networks" href="networks.html" /><link rel="prev" title="Communications Hardware" href="hardware.html" />
<meta name="generator" content="sphinx-5.3.0, furo 2022.09.29.dev1"/> <meta name="generator" content="sphinx-5.3.0, furo 2022.09.29.dev1"/>
<title>Configuring Interfaces - Reticulum Network Stack 0.8.2 beta documentation</title> <title>Configuring Interfaces - Reticulum Network Stack 0.8.4 beta documentation</title>
<link rel="stylesheet" type="text/css" href="_static/pygments.css" /> <link rel="stylesheet" type="text/css" href="_static/pygments.css" />
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=189ec851f9bb375a2509b67be1f64f0cf212b702" /> <link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=189ec851f9bb375a2509b67be1f64f0cf212b702" />
<link rel="stylesheet" type="text/css" href="_static/copybutton.css" /> <link rel="stylesheet" type="text/css" href="_static/copybutton.css" />
@ -141,7 +141,7 @@
</label> </label>
</div> </div>
<div class="header-center"> <div class="header-center">
<a href="index.html"><div class="brand">Reticulum Network Stack 0.8.2 beta documentation</div></a> <a href="index.html"><div class="brand">Reticulum Network Stack 0.8.4 beta documentation</div></a>
</div> </div>
<div class="header-right"> <div class="header-right">
<div class="theme-toggle-container theme-toggle-header"> <div class="theme-toggle-container theme-toggle-header">
@ -167,7 +167,7 @@
<img class="sidebar-logo" src="_static/rns_logo_512.png" alt="Logo"/> <img class="sidebar-logo" src="_static/rns_logo_512.png" alt="Logo"/>
</div> </div>
<span class="sidebar-brand-text">Reticulum Network Stack 0.8.2 beta documentation</span> <span class="sidebar-brand-text">Reticulum Network Stack 0.8.4 beta documentation</span>
</a><form class="sidebar-search-container" method="get" action="search.html" role="search"> </a><form class="sidebar-search-container" method="get" action="search.html" role="search">
<input class="sidebar-search" placeholder="Search" name="q" aria-label="Search"> <input class="sidebar-search" placeholder="Search" name="q" aria-label="Search">

View File

@ -6,7 +6,7 @@
<link rel="index" title="Index" href="genindex.html" /><link rel="search" title="Search" href="search.html" /><link rel="next" title="Code Examples" href="examples.html" /><link rel="prev" title="Configuring Interfaces" href="interfaces.html" /> <link rel="index" title="Index" href="genindex.html" /><link rel="search" title="Search" href="search.html" /><link rel="next" title="Code Examples" href="examples.html" /><link rel="prev" title="Configuring Interfaces" href="interfaces.html" />
<meta name="generator" content="sphinx-5.3.0, furo 2022.09.29.dev1"/> <meta name="generator" content="sphinx-5.3.0, furo 2022.09.29.dev1"/>
<title>Building Networks - Reticulum Network Stack 0.8.2 beta documentation</title> <title>Building Networks - Reticulum Network Stack 0.8.4 beta documentation</title>
<link rel="stylesheet" type="text/css" href="_static/pygments.css" /> <link rel="stylesheet" type="text/css" href="_static/pygments.css" />
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=189ec851f9bb375a2509b67be1f64f0cf212b702" /> <link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=189ec851f9bb375a2509b67be1f64f0cf212b702" />
<link rel="stylesheet" type="text/css" href="_static/copybutton.css" /> <link rel="stylesheet" type="text/css" href="_static/copybutton.css" />
@ -141,7 +141,7 @@
</label> </label>
</div> </div>
<div class="header-center"> <div class="header-center">
<a href="index.html"><div class="brand">Reticulum Network Stack 0.8.2 beta documentation</div></a> <a href="index.html"><div class="brand">Reticulum Network Stack 0.8.4 beta documentation</div></a>
</div> </div>
<div class="header-right"> <div class="header-right">
<div class="theme-toggle-container theme-toggle-header"> <div class="theme-toggle-container theme-toggle-header">
@ -167,7 +167,7 @@
<img class="sidebar-logo" src="_static/rns_logo_512.png" alt="Logo"/> <img class="sidebar-logo" src="_static/rns_logo_512.png" alt="Logo"/>
</div> </div>
<span class="sidebar-brand-text">Reticulum Network Stack 0.8.2 beta documentation</span> <span class="sidebar-brand-text">Reticulum Network Stack 0.8.4 beta documentation</span>
</a><form class="sidebar-search-container" method="get" action="search.html" role="search"> </a><form class="sidebar-search-container" method="get" action="search.html" role="search">
<input class="sidebar-search" placeholder="Search" name="q" aria-label="Search"> <input class="sidebar-search" placeholder="Search" name="q" aria-label="Search">

Binary file not shown.

View File

@ -6,7 +6,7 @@
<link rel="index" title="Index" href="genindex.html" /><link rel="search" title="Search" href="search.html" /><link rel="prev" title="Support Reticulum" href="support.html" /> <link rel="index" title="Index" href="genindex.html" /><link rel="search" title="Search" href="search.html" /><link rel="prev" title="Support Reticulum" href="support.html" />
<meta name="generator" content="sphinx-5.3.0, furo 2022.09.29.dev1"/> <meta name="generator" content="sphinx-5.3.0, furo 2022.09.29.dev1"/>
<title>API Reference - Reticulum Network Stack 0.8.2 beta documentation</title> <title>API Reference - Reticulum Network Stack 0.8.4 beta documentation</title>
<link rel="stylesheet" type="text/css" href="_static/pygments.css" /> <link rel="stylesheet" type="text/css" href="_static/pygments.css" />
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=189ec851f9bb375a2509b67be1f64f0cf212b702" /> <link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=189ec851f9bb375a2509b67be1f64f0cf212b702" />
<link rel="stylesheet" type="text/css" href="_static/copybutton.css" /> <link rel="stylesheet" type="text/css" href="_static/copybutton.css" />
@ -141,7 +141,7 @@
</label> </label>
</div> </div>
<div class="header-center"> <div class="header-center">
<a href="index.html"><div class="brand">Reticulum Network Stack 0.8.2 beta documentation</div></a> <a href="index.html"><div class="brand">Reticulum Network Stack 0.8.4 beta documentation</div></a>
</div> </div>
<div class="header-right"> <div class="header-right">
<div class="theme-toggle-container theme-toggle-header"> <div class="theme-toggle-container theme-toggle-header">
@ -167,7 +167,7 @@
<img class="sidebar-logo" src="_static/rns_logo_512.png" alt="Logo"/> <img class="sidebar-logo" src="_static/rns_logo_512.png" alt="Logo"/>
</div> </div>
<span class="sidebar-brand-text">Reticulum Network Stack 0.8.2 beta documentation</span> <span class="sidebar-brand-text">Reticulum Network Stack 0.8.4 beta documentation</span>
</a><form class="sidebar-search-container" method="get" action="search.html" role="search"> </a><form class="sidebar-search-container" method="get" action="search.html" role="search">
<input class="sidebar-search" placeholder="Search" name="q" aria-label="Search"> <input class="sidebar-search" placeholder="Search" name="q" aria-label="Search">

View File

@ -4,7 +4,7 @@
<meta name="viewport" content="width=device-width,initial-scale=1"/> <meta name="viewport" content="width=device-width,initial-scale=1"/>
<meta name="color-scheme" content="light dark"><link rel="index" title="Index" href="genindex.html" /><link rel="search" title="Search" href="#" /> <meta name="color-scheme" content="light dark"><link rel="index" title="Index" href="genindex.html" /><link rel="search" title="Search" href="#" />
<meta name="generator" content="sphinx-5.3.0, furo 2022.09.29.dev1"/><title>Search - Reticulum Network Stack 0.8.2 beta documentation</title><link rel="stylesheet" type="text/css" href="_static/pygments.css" /> <meta name="generator" content="sphinx-5.3.0, furo 2022.09.29.dev1"/><title>Search - Reticulum Network Stack 0.8.4 beta documentation</title><link rel="stylesheet" type="text/css" href="_static/pygments.css" />
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=189ec851f9bb375a2509b67be1f64f0cf212b702" /> <link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=189ec851f9bb375a2509b67be1f64f0cf212b702" />
<link rel="stylesheet" type="text/css" href="_static/copybutton.css" /> <link rel="stylesheet" type="text/css" href="_static/copybutton.css" />
<link rel="stylesheet" type="text/css" href="_static/styles/furo-extensions.css?digest=30d1aed668e5c3a91c3e3bf6a60b675221979f0e" /> <link rel="stylesheet" type="text/css" href="_static/styles/furo-extensions.css?digest=30d1aed668e5c3a91c3e3bf6a60b675221979f0e" />
@ -138,7 +138,7 @@
</label> </label>
</div> </div>
<div class="header-center"> <div class="header-center">
<a href="index.html"><div class="brand">Reticulum Network Stack 0.8.2 beta documentation</div></a> <a href="index.html"><div class="brand">Reticulum Network Stack 0.8.4 beta documentation</div></a>
</div> </div>
<div class="header-right"> <div class="header-right">
<div class="theme-toggle-container theme-toggle-header"> <div class="theme-toggle-container theme-toggle-header">
@ -164,7 +164,7 @@
<img class="sidebar-logo" src="_static/rns_logo_512.png" alt="Logo"/> <img class="sidebar-logo" src="_static/rns_logo_512.png" alt="Logo"/>
</div> </div>
<span class="sidebar-brand-text">Reticulum Network Stack 0.8.2 beta documentation</span> <span class="sidebar-brand-text">Reticulum Network Stack 0.8.4 beta documentation</span>
</a><form class="sidebar-search-container" method="get" action="#" role="search"> </a><form class="sidebar-search-container" method="get" action="#" role="search">
<input class="sidebar-search" placeholder="Search" name="q" aria-label="Search"> <input class="sidebar-search" placeholder="Search" name="q" aria-label="Search">

View File

@ -6,7 +6,7 @@
<link rel="index" title="Index" href="genindex.html" /><link rel="search" title="Search" href="search.html" /><link rel="next" title="API Reference" href="reference.html" /><link rel="prev" title="Code Examples" href="examples.html" /> <link rel="index" title="Index" href="genindex.html" /><link rel="search" title="Search" href="search.html" /><link rel="next" title="API Reference" href="reference.html" /><link rel="prev" title="Code Examples" href="examples.html" />
<meta name="generator" content="sphinx-5.3.0, furo 2022.09.29.dev1"/> <meta name="generator" content="sphinx-5.3.0, furo 2022.09.29.dev1"/>
<title>Support Reticulum - Reticulum Network Stack 0.8.2 beta documentation</title> <title>Support Reticulum - Reticulum Network Stack 0.8.4 beta documentation</title>
<link rel="stylesheet" type="text/css" href="_static/pygments.css" /> <link rel="stylesheet" type="text/css" href="_static/pygments.css" />
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=189ec851f9bb375a2509b67be1f64f0cf212b702" /> <link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=189ec851f9bb375a2509b67be1f64f0cf212b702" />
<link rel="stylesheet" type="text/css" href="_static/copybutton.css" /> <link rel="stylesheet" type="text/css" href="_static/copybutton.css" />
@ -141,7 +141,7 @@
</label> </label>
</div> </div>
<div class="header-center"> <div class="header-center">
<a href="index.html"><div class="brand">Reticulum Network Stack 0.8.2 beta documentation</div></a> <a href="index.html"><div class="brand">Reticulum Network Stack 0.8.4 beta documentation</div></a>
</div> </div>
<div class="header-right"> <div class="header-right">
<div class="theme-toggle-container theme-toggle-header"> <div class="theme-toggle-container theme-toggle-header">
@ -167,7 +167,7 @@
<img class="sidebar-logo" src="_static/rns_logo_512.png" alt="Logo"/> <img class="sidebar-logo" src="_static/rns_logo_512.png" alt="Logo"/>
</div> </div>
<span class="sidebar-brand-text">Reticulum Network Stack 0.8.2 beta documentation</span> <span class="sidebar-brand-text">Reticulum Network Stack 0.8.4 beta documentation</span>
</a><form class="sidebar-search-container" method="get" action="search.html" role="search"> </a><form class="sidebar-search-container" method="get" action="search.html" role="search">
<input class="sidebar-search" placeholder="Search" name="q" aria-label="Search"> <input class="sidebar-search" placeholder="Search" name="q" aria-label="Search">

View File

@ -6,7 +6,7 @@
<link rel="index" title="Index" href="genindex.html" /><link rel="search" title="Search" href="search.html" /><link rel="next" title="Communications Hardware" href="hardware.html" /><link rel="prev" title="Using Reticulum on Your System" href="using.html" /> <link rel="index" title="Index" href="genindex.html" /><link rel="search" title="Search" href="search.html" /><link rel="next" title="Communications Hardware" href="hardware.html" /><link rel="prev" title="Using Reticulum on Your System" href="using.html" />
<meta name="generator" content="sphinx-5.3.0, furo 2022.09.29.dev1"/> <meta name="generator" content="sphinx-5.3.0, furo 2022.09.29.dev1"/>
<title>Understanding Reticulum - Reticulum Network Stack 0.8.2 beta documentation</title> <title>Understanding Reticulum - Reticulum Network Stack 0.8.4 beta documentation</title>
<link rel="stylesheet" type="text/css" href="_static/pygments.css" /> <link rel="stylesheet" type="text/css" href="_static/pygments.css" />
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=189ec851f9bb375a2509b67be1f64f0cf212b702" /> <link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=189ec851f9bb375a2509b67be1f64f0cf212b702" />
<link rel="stylesheet" type="text/css" href="_static/copybutton.css" /> <link rel="stylesheet" type="text/css" href="_static/copybutton.css" />
@ -141,7 +141,7 @@
</label> </label>
</div> </div>
<div class="header-center"> <div class="header-center">
<a href="index.html"><div class="brand">Reticulum Network Stack 0.8.2 beta documentation</div></a> <a href="index.html"><div class="brand">Reticulum Network Stack 0.8.4 beta documentation</div></a>
</div> </div>
<div class="header-right"> <div class="header-right">
<div class="theme-toggle-container theme-toggle-header"> <div class="theme-toggle-container theme-toggle-header">
@ -167,7 +167,7 @@
<img class="sidebar-logo" src="_static/rns_logo_512.png" alt="Logo"/> <img class="sidebar-logo" src="_static/rns_logo_512.png" alt="Logo"/>
</div> </div>
<span class="sidebar-brand-text">Reticulum Network Stack 0.8.2 beta documentation</span> <span class="sidebar-brand-text">Reticulum Network Stack 0.8.4 beta documentation</span>
</a><form class="sidebar-search-container" method="get" action="search.html" role="search"> </a><form class="sidebar-search-container" method="get" action="search.html" role="search">
<input class="sidebar-search" placeholder="Search" name="q" aria-label="Search"> <input class="sidebar-search" placeholder="Search" name="q" aria-label="Search">

View File

@ -6,7 +6,7 @@
<link rel="index" title="Index" href="genindex.html" /><link rel="search" title="Search" href="search.html" /><link rel="next" title="Understanding Reticulum" href="understanding.html" /><link rel="prev" title="Getting Started Fast" href="gettingstartedfast.html" /> <link rel="index" title="Index" href="genindex.html" /><link rel="search" title="Search" href="search.html" /><link rel="next" title="Understanding Reticulum" href="understanding.html" /><link rel="prev" title="Getting Started Fast" href="gettingstartedfast.html" />
<meta name="generator" content="sphinx-5.3.0, furo 2022.09.29.dev1"/> <meta name="generator" content="sphinx-5.3.0, furo 2022.09.29.dev1"/>
<title>Using Reticulum on Your System - Reticulum Network Stack 0.8.2 beta documentation</title> <title>Using Reticulum on Your System - Reticulum Network Stack 0.8.4 beta documentation</title>
<link rel="stylesheet" type="text/css" href="_static/pygments.css" /> <link rel="stylesheet" type="text/css" href="_static/pygments.css" />
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=189ec851f9bb375a2509b67be1f64f0cf212b702" /> <link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=189ec851f9bb375a2509b67be1f64f0cf212b702" />
<link rel="stylesheet" type="text/css" href="_static/copybutton.css" /> <link rel="stylesheet" type="text/css" href="_static/copybutton.css" />
@ -141,7 +141,7 @@
</label> </label>
</div> </div>
<div class="header-center"> <div class="header-center">
<a href="index.html"><div class="brand">Reticulum Network Stack 0.8.2 beta documentation</div></a> <a href="index.html"><div class="brand">Reticulum Network Stack 0.8.4 beta documentation</div></a>
</div> </div>
<div class="header-right"> <div class="header-right">
<div class="theme-toggle-container theme-toggle-header"> <div class="theme-toggle-container theme-toggle-header">
@ -167,7 +167,7 @@
<img class="sidebar-logo" src="_static/rns_logo_512.png" alt="Logo"/> <img class="sidebar-logo" src="_static/rns_logo_512.png" alt="Logo"/>
</div> </div>
<span class="sidebar-brand-text">Reticulum Network Stack 0.8.2 beta documentation</span> <span class="sidebar-brand-text">Reticulum Network Stack 0.8.4 beta documentation</span>
</a><form class="sidebar-search-container" method="get" action="search.html" role="search"> </a><form class="sidebar-search-container" method="get" action="search.html" role="search">
<input class="sidebar-search" placeholder="Search" name="q" aria-label="Search"> <input class="sidebar-search" placeholder="Search" name="q" aria-label="Search">

View File

@ -6,7 +6,7 @@
<link rel="index" title="Index" href="genindex.html" /><link rel="search" title="Search" href="search.html" /><link rel="next" title="Getting Started Fast" href="gettingstartedfast.html" /><link rel="prev" title="Reticulum Network Stack Manual" href="index.html" /> <link rel="index" title="Index" href="genindex.html" /><link rel="search" title="Search" href="search.html" /><link rel="next" title="Getting Started Fast" href="gettingstartedfast.html" /><link rel="prev" title="Reticulum Network Stack Manual" href="index.html" />
<meta name="generator" content="sphinx-5.3.0, furo 2022.09.29.dev1"/> <meta name="generator" content="sphinx-5.3.0, furo 2022.09.29.dev1"/>
<title>What is Reticulum? - Reticulum Network Stack 0.8.2 beta documentation</title> <title>What is Reticulum? - Reticulum Network Stack 0.8.4 beta documentation</title>
<link rel="stylesheet" type="text/css" href="_static/pygments.css" /> <link rel="stylesheet" type="text/css" href="_static/pygments.css" />
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=189ec851f9bb375a2509b67be1f64f0cf212b702" /> <link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=189ec851f9bb375a2509b67be1f64f0cf212b702" />
<link rel="stylesheet" type="text/css" href="_static/copybutton.css" /> <link rel="stylesheet" type="text/css" href="_static/copybutton.css" />
@ -141,7 +141,7 @@
</label> </label>
</div> </div>
<div class="header-center"> <div class="header-center">
<a href="index.html"><div class="brand">Reticulum Network Stack 0.8.2 beta documentation</div></a> <a href="index.html"><div class="brand">Reticulum Network Stack 0.8.4 beta documentation</div></a>
</div> </div>
<div class="header-right"> <div class="header-right">
<div class="theme-toggle-container theme-toggle-header"> <div class="theme-toggle-container theme-toggle-header">
@ -167,7 +167,7 @@
<img class="sidebar-logo" src="_static/rns_logo_512.png" alt="Logo"/> <img class="sidebar-logo" src="_static/rns_logo_512.png" alt="Logo"/>
</div> </div>
<span class="sidebar-brand-text">Reticulum Network Stack 0.8.2 beta documentation</span> <span class="sidebar-brand-text">Reticulum Network Stack 0.8.4 beta documentation</span>
</a><form class="sidebar-search-container" method="get" action="search.html" role="search"> </a><form class="sidebar-search-container" method="get" action="search.html" role="search">
<input class="sidebar-search" placeholder="Search" name="q" aria-label="Search"> <input class="sidebar-search" placeholder="Search" name="q" aria-label="Search">

View File

@ -13,12 +13,12 @@ project = 'Reticulum Network Stack'
copyright = '2023, Mark Qvist' copyright = '2023, Mark Qvist'
author = 'Mark Qvist' author = 'Mark Qvist'
exec(open("../../RNS/_version.py", "r").read()) exec(open("../../RNS/_version.py").read())
version = __version__ version = __version__
# The full version, including alpha/beta/rc tags # The full version, including alpha/beta/rc tags
import RNS import RNS
release = RNS._version.__version__+" beta" release = f"{RNS._version.__version__} beta"
# -- General configuration --------------------------------------------------- # -- General configuration ---------------------------------------------------
extensions = [ extensions = [

View File

@ -9,14 +9,14 @@ if '--pure' in sys.argv:
sys.argv.remove('--pure') sys.argv.remove('--pure')
print("Building pure-python wheel") print("Building pure-python wheel")
exec(open("RNS/_version.py", "r").read()) exec(open("RNS/_version.py").read())
with open("README.md", "r") as fh: with open("README.md") as fh:
long_description = fh.read() long_description = fh.read()
if pure_python: if pure_python:
pkg_name = "rnspure" pkg_name = "rnspure"
requirements = [] requirements = []
long_description = long_description.replace("</p>", "</p>"+pure_notice) long_description = long_description.replace("</p>", f"</p>{pure_notice}")
else: else:
pkg_name = "rns" pkg_name = "rns"
requirements = ['cryptography>=3.4.7', 'pyserial>=3.5'] requirements = ['cryptography>=3.4.7', 'pyserial>=3.5']

View File

@ -145,7 +145,7 @@ class SystemMessage(MessageBase):
MSGTYPE = 0xf000 MSGTYPE = 0xf000
def pack(self) -> bytes: def pack(self) -> bytes:
return bytes() return b''
def unpack(self, raw): def unpack(self, raw):
pass pass
@ -160,7 +160,7 @@ class ProtocolHarness(contextlib.AbstractContextManager):
def cleanup(self): def cleanup(self):
self.channel._shutdown() self.channel._shutdown()
def __exit__(self, __exc_type: typing.Type[BaseException], __exc_value: BaseException, def __exit__(self, __exc_type: type[BaseException], __exc_value: BaseException,
__traceback: types.TracebackType) -> bool: __traceback: types.TracebackType) -> bool:
# self._log.debug(f"__exit__({__exc_type}, {__exc_value}, {__traceback})") # self._log.debug(f"__exit__({__exc_type}, {__exc_value}, {__traceback})")
self.cleanup() self.cleanup()
@ -431,7 +431,7 @@ class TestChannel(unittest.TestCase):
self.assertEqual(len(data), count) self.assertEqual(len(data), count)
read_finished = False read_finished = False
result = bytes() result = b''
def read_thread(): def read_thread():
nonlocal read_finished, result nonlocal read_finished, result

View File

@ -11,22 +11,22 @@ class TestSHA256(unittest.TestCase):
def test_empty(self): def test_empty(self):
self.assertEqual( self.assertEqual(
self.f(''.encode("utf-8")), self.f(b''),
bytes.fromhex("e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855")) bytes.fromhex("e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"))
def test_less_than_block_length(self): def test_less_than_block_length(self):
self.assertEqual( self.assertEqual(
self.f('abc'.encode("utf-8")), self.f(b'abc'),
bytes.fromhex("ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad")) bytes.fromhex("ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad"))
def test_block_length(self): def test_block_length(self):
self.assertEqual( self.assertEqual(
self.f('a'.encode("utf-8")*64), self.f(b'a'*64),
bytes.fromhex("ffe054fe7ae0cb6dc65c3af9b61d5209f439851db43d0ba5997337df154668eb")) bytes.fromhex("ffe054fe7ae0cb6dc65c3af9b61d5209f439851db43d0ba5997337df154668eb"))
def test_several_blocks(self): def test_several_blocks(self):
self.assertEqual( self.assertEqual(
self.f('a'.encode("utf-8")*1000000), self.f(b'a'*1000000),
bytes.fromhex("cdc76e5c9914fb9281a1c7e284d73e67f1809a48a497200e046d39ccc7112cd0")) bytes.fromhex("cdc76e5c9914fb9281a1c7e284d73e67f1809a48a497200e046d39ccc7112cd0"))
def test_random_blocks(self): def test_random_blocks(self):
@ -49,10 +49,10 @@ class TestSHA256(unittest.TestCase):
if (i%1000 == 0): if (i%1000 == 0):
gbytes = round(b/1000000000,3) gbytes = round(b/1000000000,3)
mbps = round((b*8/1000000)/(time.time()-start), 2) mbps = round((b*8/1000000)/(time.time()-start), 2)
print(str(i)+" rounds OK, total data: "+str(gbytes)+"GB, "+str(mbps)+"mbps") print(f"{i} rounds OK, total data: {gbytes}GB, {mbps}mbps")
if not ok: if not ok:
print("Failed at round "+str(i)) print(f"Failed at round {i}")
else: else:
print("SHA-256 test OK") print("SHA-256 test OK")
@ -65,25 +65,25 @@ class TestSHA512(unittest.TestCase):
def test_empty(self): def test_empty(self):
self.assertEqual( self.assertEqual(
self.f(''.encode("utf-8")), self.f(b''),
bytes.fromhex( bytes.fromhex(
'cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce'+ 'cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce'+
'47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e')) '47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e'))
def test_less_than_block_length(self): def test_less_than_block_length(self):
self.assertEqual(self.f('abc'.encode("utf-8")), self.assertEqual(self.f(b'abc'),
bytes.fromhex( bytes.fromhex(
'ddaf35a193617abacc417349ae20413112e6fa4e89a97ea20a9eeee64b55d39a'+ 'ddaf35a193617abacc417349ae20413112e6fa4e89a97ea20a9eeee64b55d39a'+
'2192992a274fc1a836ba3c23a3feebbd454d4423643ce80e2a9ac94fa54ca49f')) '2192992a274fc1a836ba3c23a3feebbd454d4423643ce80e2a9ac94fa54ca49f'))
def test_block_length(self): def test_block_length(self):
self.assertEqual(self.f('a'.encode("utf-8")*128), self.assertEqual(self.f(b'a'*128),
bytes.fromhex( bytes.fromhex(
'b73d1929aa615934e61a871596b3f3b33359f42b8175602e89f7e06e5f658a24'+ 'b73d1929aa615934e61a871596b3f3b33359f42b8175602e89f7e06e5f658a24'+
'3667807ed300314b95cacdd579f3e33abdfbe351909519a846d465c59582f321')) '3667807ed300314b95cacdd579f3e33abdfbe351909519a846d465c59582f321'))
def test_several_blocks(self): def test_several_blocks(self):
self.assertEqual(self.f('a'.encode("utf-8")*1000000), self.assertEqual(self.f(b'a'*1000000),
bytes.fromhex( bytes.fromhex(
'e718483d0ce769644e2e42c7bc15b4638e1f98b13b2044285632a803afa973eb'+ 'e718483d0ce769644e2e42c7bc15b4638e1f98b13b2044285632a803afa973eb'+
'de0ff244877ea60a4cb0432ce577c31beb009c5c2c49aa2e4eadb217ad8cc09b')) 'de0ff244877ea60a4cb0432ce577c31beb009c5c2c49aa2e4eadb217ad8cc09b'))
@ -108,10 +108,10 @@ class TestSHA512(unittest.TestCase):
if (i%1000 == 0): if (i%1000 == 0):
gbytes = round(b/1000000000,3) gbytes = round(b/1000000000,3)
mbps = round((b*8/1000000)/(time.time()-start), 2) mbps = round((b*8/1000000)/(time.time()-start), 2)
print(str(i)+" rounds OK, total data: "+str(gbytes)+"GB, "+str(mbps)+"mbps") print(f"{i} rounds OK, total data: {gbytes}GB, {mbps}mbps")
if not ok: if not ok:
print("Failed at round "+str(i)) print(f"Failed at round {i}")
else: else:
print("SHA-512 test OK") print("SHA-512 test OK")

View File

@ -62,8 +62,8 @@ class TestIdentity(unittest.TestCase):
tmdev = tmax - tmin tmdev = tmax - tmin
mpct = (tmax/tmed)*100 mpct = (tmax/tmed)*100
print("Random messages:") print("Random messages:")
print(" Signature timing min/avg/med/max/mdev: "+str(round(tmin, 3))+"/"+str(round(tmean, 3))+"/"+str(round(tmed, 3))+"/"+str(round(tmax, 3))+"/"+str(round(tmdev, 3))) print(f" Signature timing min/avg/med/max/mdev: {round(tmin, 3)}/{round(tmean, 3)}/{round(tmed, 3)}/{round(tmax, 3)}/{round(tmdev, 3)}")
print(" Max deviation from median: "+str(round(mpct, 1))+"%") print(f" Max deviation from median: {round(mpct, 1)}%")
print() print()
id1 = RNS.Identity() id1 = RNS.Identity()
@ -85,8 +85,8 @@ class TestIdentity(unittest.TestCase):
tmdev = tmax - tmin tmdev = tmax - tmin
mpct = (tmax/tmed)*100 mpct = (tmax/tmed)*100
print("All 0xff messages:") print("All 0xff messages:")
print(" Signature timing min/avg/med/max/mdev: "+str(round(tmin, 3))+"/"+str(round(tmean, 3))+"/"+str(round(tmed, 3))+"/"+str(round(tmax, 3))+"/"+str(round(tmdev, 3))) print(f" Signature timing min/avg/med/max/mdev: {round(tmin, 3)}/{round(tmean, 3)}/{round(tmed, 3)}/{round(tmax, 3)}/{round(tmdev, 3)}")
print(" Max deviation from median: "+str(round(mpct, 1))+"%") print(f" Max deviation from median: {round(mpct, 1)}%")
print() print()
id1 = RNS.Identity() id1 = RNS.Identity()
@ -108,8 +108,8 @@ class TestIdentity(unittest.TestCase):
tmdev = tmax - tmin tmdev = tmax - tmin
mpct = (tmax/tmed)*100 mpct = (tmax/tmed)*100
print("All 0x00 messages:") print("All 0x00 messages:")
print(" Signature timing min/avg/med/max/mdev: "+str(round(tmin, 3))+"/"+str(round(tmean, 3))+"/"+str(round(tmed, 3))+"/"+str(round(tmax, 3))+"/"+str(round(tmdev, 3))) print(f" Signature timing min/avg/med/max/mdev: {round(tmin, 3)}/{round(tmean, 3)}/{round(tmed, 3)}/{round(tmax, 3)}/{round(tmdev, 3)}")
print(" Max deviation from median: "+str(round(mpct, 1))+"%") print(f" Max deviation from median: {round(mpct, 1)}%")
print() print()
b = 0 b = 0
@ -127,7 +127,7 @@ class TestIdentity(unittest.TestCase):
self.assertEqual(True, id2.validate(signature, msg)) self.assertEqual(True, id2.validate(signature, msg))
t += time.time() - start t += time.time() - start
print("Sign/validate chunks < MTU: "+self.size_str(b/t, "b")+"ps") print(f"Sign/validate chunks < MTU: {self.size_str(b / t, 'b')}ps")
for i in range(1, 500): for i in range(1, 500):
mlen = 16*1024 mlen = 16*1024
@ -142,7 +142,7 @@ class TestIdentity(unittest.TestCase):
self.assertEqual(True, id2.validate(signature, msg)) self.assertEqual(True, id2.validate(signature, msg))
t += time.time() - start t += time.time() - start
print("Sign/validate 16KB chunks: "+self.size_str(b/t, "b")+"ps") print(f"Sign/validate 16KB chunks: {self.size_str(b / t, 'b')}ps")
def test_2_encrypt(self): def test_2_encrypt(self):
@ -176,8 +176,8 @@ class TestIdentity(unittest.TestCase):
self.assertEqual(msg, decrypted) self.assertEqual(msg, decrypted)
d_t += time.time() - d_start d_t += time.time() - d_start
print("Encrypt chunks < MTU: "+self.size_str(b/e_t, "b")+"ps") print(f"Encrypt chunks < MTU: {self.size_str(b / e_t, 'b')}ps")
print("Decrypt chunks < MTU: "+self.size_str(b/d_t, "b")+"ps") print(f"Decrypt chunks < MTU: {self.size_str(b / d_t, 'b')}ps")
print("") print("")
# Test encrypt and decrypt of large chunks # Test encrypt and decrypt of large chunks
@ -203,8 +203,8 @@ class TestIdentity(unittest.TestCase):
self.assertEqual(msg, id1.decrypt(token)) self.assertEqual(msg, id1.decrypt(token))
d_t += time.time() - d_start d_t += time.time() - d_start
print("Encrypt "+self.size_str(mlen)+" chunks: "+self.size_str(b/e_t, "b")+"ps") print(f"Encrypt {self.size_str(mlen)} chunks: {self.size_str(b / e_t, 'b')}ps")
print("Decrypt "+self.size_str(mlen)+" chunks: "+self.size_str(b/d_t, "b")+"ps") print(f"Decrypt {self.size_str(mlen)} chunks: {self.size_str(b / d_t, 'b')}ps")
def size_str(self, num, suffix='B'): def size_str(self, num, suffix='B'):
units = ['','K','M','G','T','P','E','Z'] units = ['','K','M','G','T','P','E','Z']
@ -218,12 +218,12 @@ class TestIdentity(unittest.TestCase):
for unit in units: for unit in units:
if abs(num) < 1000.0: if abs(num) < 1000.0:
if unit == "": if unit == "":
return "%.0f %s%s" % (num, unit, suffix) return f"{num:.0f} {unit}{suffix}"
else: else:
return "%.2f %s%s" % (num, unit, suffix) return f"{num:.2f} {unit}{suffix}"
num /= 1000.0 num /= 1000.0
return "%.2f%s%s" % (num, last_unit, suffix) return f"{num:.2f}{last_unit}{suffix}"
if __name__ == '__main__': if __name__ == '__main__':
unittest.main(verbosity=2) unittest.main(verbosity=2)

View File

@ -28,7 +28,7 @@ BUFFER_TEST_TARGET = 32000
def targets_job(caller): def targets_job(caller):
cmd = "python -c \"from tests.link import targets; targets()\"" cmd = "python -c \"from tests.link import targets; targets()\""
print("Opening subprocess for "+str(cmd)+"...", RNS.LOG_VERBOSE) print(f"Opening subprocess for {cmd}...", RNS.LOG_VERBOSE)
ppath = os.getcwd() ppath = os.getcwd()
try: try:
@ -144,7 +144,7 @@ class TestLink(unittest.TestCase):
packet_size = RNS.Link.MDU packet_size = RNS.Link.MDU
pstart = time.time() pstart = time.time()
print("Sending "+str(num_packets)+" link packets of "+str(packet_size)+" bytes...") print(f"Sending {num_packets} link packets of {packet_size} bytes...")
for i in range(0, num_packets): for i in range(0, num_packets):
time.sleep(0.003) time.sleep(0.003)
b += packet_size b += packet_size
@ -154,7 +154,7 @@ class TestLink(unittest.TestCase):
receipts.append(p.send()) receipts.append(p.send())
pr_t += time.time() - start pr_t += time.time() - start
print("Sent "+self.size_str(b)+", "+self.size_str(b/pr_t, "b")+"ps") print(f"Sent {self.size_str(b)}, {self.size_str(b / pr_t, 'b')}ps")
print("Checking receipts...", end=" ") print("Checking receipts...", end=" ")
all_ok = False all_ok = False
@ -175,11 +175,11 @@ class TestLink(unittest.TestCase):
if n_failed > 0: if n_failed > 0:
ns = "s" if n_failed != 1 else "" ns = "s" if n_failed != 1 else ""
print("Failed to receive proof for "+str(n_failed)+" packet"+ns) print(f"Failed to receive proof for {n_failed} packet{ns}")
self.assertEqual(all_ok, True) self.assertEqual(all_ok, True)
print("OK!") print("OK!")
print("Single packet and proof round-trip throughput is "+self.size_str(b/pduration, "b")+"ps") print(f"Single packet and proof round-trip throughput is {self.size_str(b / pduration, 'b')}ps")
l1.teardown() l1.teardown()
time.sleep(0.5) time.sleep(0.5)
@ -209,7 +209,7 @@ class TestLink(unittest.TestCase):
resource_timeout = 120 resource_timeout = 120
resource_size = 128 resource_size = 128
data = os.urandom(resource_size) data = os.urandom(resource_size)
print("Sending "+self.size_str(resource_size)+" resource...") print(f"Sending {self.size_str(resource_size)} resource...")
resource = RNS.Resource(data, l1, timeout=resource_timeout) resource = RNS.Resource(data, l1, timeout=resource_timeout)
start = time.time() start = time.time()
@ -219,7 +219,7 @@ class TestLink(unittest.TestCase):
t = time.time() - start t = time.time() - start
self.assertEqual(resource.status, RNS.Resource.COMPLETE) self.assertEqual(resource.status, RNS.Resource.COMPLETE)
print("Resource completed at "+self.size_str(resource_size/t, "b")+"ps") print(f"Resource completed at {self.size_str(resource_size / t, 'b')}ps")
l1.teardown() l1.teardown()
time.sleep(0.5) time.sleep(0.5)
@ -249,7 +249,7 @@ class TestLink(unittest.TestCase):
resource_timeout = 120 resource_timeout = 120
resource_size = 256*1000 resource_size = 256*1000
data = os.urandom(resource_size) data = os.urandom(resource_size)
print("Sending "+self.size_str(resource_size)+" resource...") print(f"Sending {self.size_str(resource_size)} resource...")
resource = RNS.Resource(data, l1, timeout=resource_timeout) resource = RNS.Resource(data, l1, timeout=resource_timeout)
start = time.time() start = time.time()
@ -259,7 +259,7 @@ class TestLink(unittest.TestCase):
t = time.time() - start t = time.time() - start
self.assertEqual(resource.status, RNS.Resource.COMPLETE) self.assertEqual(resource.status, RNS.Resource.COMPLETE)
print("Resource completed at "+self.size_str(resource_size/t, "b")+"ps") print(f"Resource completed at {self.size_str(resource_size / t, 'b')}ps")
l1.teardown() l1.teardown()
time.sleep(0.5) time.sleep(0.5)
@ -288,7 +288,7 @@ class TestLink(unittest.TestCase):
resource_timeout = 120 resource_timeout = 120
resource_size = 1000*1000 resource_size = 1000*1000
data = os.urandom(resource_size) data = os.urandom(resource_size)
print("Sending "+self.size_str(resource_size)+" resource...") print(f"Sending {self.size_str(resource_size)} resource...")
resource = RNS.Resource(data, l1, timeout=resource_timeout) resource = RNS.Resource(data, l1, timeout=resource_timeout)
start = time.time() start = time.time()
@ -298,7 +298,7 @@ class TestLink(unittest.TestCase):
t = time.time() - start t = time.time() - start
self.assertEqual(resource.status, RNS.Resource.COMPLETE) self.assertEqual(resource.status, RNS.Resource.COMPLETE)
print("Resource completed at "+self.size_str(resource_size/t, "b")+"ps") print(f"Resource completed at {self.size_str(resource_size / t, 'b')}ps")
l1.teardown() l1.teardown()
time.sleep(0.5) time.sleep(0.5)
@ -332,7 +332,7 @@ class TestLink(unittest.TestCase):
resource_timeout = 120 resource_timeout = 120
resource_size = 5*1000*1000 resource_size = 5*1000*1000
data = os.urandom(resource_size) data = os.urandom(resource_size)
print("Sending "+self.size_str(resource_size)+" resource...") print(f"Sending {self.size_str(resource_size)} resource...")
resource = RNS.Resource(data, l1, timeout=resource_timeout) resource = RNS.Resource(data, l1, timeout=resource_timeout)
start = time.time() start = time.time()
@ -342,7 +342,7 @@ class TestLink(unittest.TestCase):
t = time.time() - start t = time.time() - start
self.assertEqual(resource.status, RNS.Resource.COMPLETE) self.assertEqual(resource.status, RNS.Resource.COMPLETE)
print("Resource completed at "+self.size_str(resource_size/t, "b")+"ps") print(f"Resource completed at {self.size_str(resource_size / t, 'b')}ps")
l1.teardown() l1.teardown()
time.sleep(0.5) time.sleep(0.5)
@ -379,7 +379,7 @@ class TestLink(unittest.TestCase):
resource_timeout = 120 resource_timeout = 120
resource_size = 50*1000*1000 resource_size = 50*1000*1000
data = os.urandom(resource_size) data = os.urandom(resource_size)
print("Sending "+self.size_str(resource_size)+" resource...") print(f"Sending {self.size_str(resource_size)} resource...")
resource = RNS.Resource(data, l1, timeout=resource_timeout, callback=self.lr_callback) resource = RNS.Resource(data, l1, timeout=resource_timeout, callback=self.lr_callback)
start = time.time() start = time.time()
@ -389,7 +389,7 @@ class TestLink(unittest.TestCase):
t = time.time() - start t = time.time() - start
self.assertEqual(TestLink.large_resource_status, RNS.Resource.COMPLETE) self.assertEqual(TestLink.large_resource_status, RNS.Resource.COMPLETE)
print("Resource completed at "+self.size_str(resource_size/t, "b")+"ps") print(f"Resource completed at {self.size_str(resource_size / t, 'b')}ps")
l1.teardown() l1.teardown()
time.sleep(0.5) time.sleep(0.5)
@ -478,7 +478,7 @@ class TestLink(unittest.TestCase):
channel = l1.get_channel() channel = l1.get_channel()
buffer = RNS.Buffer.create_bidirectional_buffer(0, 0, channel, handle_data) buffer = RNS.Buffer.create_bidirectional_buffer(0, 0, channel, handle_data)
buffer.write("Hi there".encode("utf-8")) buffer.write(b"Hi there")
buffer.flush() buffer.flush()
time.sleep(0.5) time.sleep(0.5)
@ -508,7 +508,7 @@ class TestLink(unittest.TestCase):
if local_bitrate is not None: if local_bitrate is not None:
local_interface.bitrate = local_bitrate local_interface.bitrate = local_bitrate
local_interface._force_bitrate = True local_interface._force_bitrate = True
print("Forcing local bitrate of " + str(local_bitrate) + " bps (" + str(round(local_bitrate/8, 0)) + " B/s)") print(f"Forcing local bitrate of {local_bitrate} bps ({round(local_bitrate / 8, 0)} B/s)")
# TODO: Load this from public bytes only # TODO: Load this from public bytes only
id1 = RNS.Identity.from_bytes(bytes.fromhex(fixed_keys[0][0])) id1 = RNS.Identity.from_bytes(bytes.fromhex(fixed_keys[0][0]))
@ -525,7 +525,7 @@ class TestLink(unittest.TestCase):
# delay a reasonable time for link to come up at current bitrate # delay a reasonable time for link to come up at current bitrate
link_sleep = max(RNS.Link.MDU * 3 / local_interface.bitrate * 8, 2) link_sleep = max(RNS.Link.MDU * 3 / local_interface.bitrate * 8, 2)
timeout_at = time.time() + link_sleep timeout_at = time.time() + link_sleep
print("Waiting " + str(round(link_sleep, 1)) + " sec for link to come up") print(f"Waiting {round(link_sleep, 1)} sec for link to come up")
while l1.status != RNS.Link.ACTIVE and time.time() < timeout_at: while l1.status != RNS.Link.ACTIVE and time.time() < timeout_at:
time.sleep(0.01) time.sleep(0.01)
@ -564,14 +564,14 @@ class TestLink(unittest.TestCase):
expected_rx_message = b"" expected_rx_message = b""
for i in range(0, len(message)): for i in range(0, len(message)):
if i > 0 and (i % StreamDataMessage.MAX_DATA_LEN) == 0: if i > 0 and (i % StreamDataMessage.MAX_DATA_LEN) == 0:
expected_rx_message += " back at you".encode("utf-8") expected_rx_message += b" back at you"
expected_rx_message += bytes([message[i]]) expected_rx_message += bytes([message[i]])
expected_rx_message += " back at you".encode("utf-8") expected_rx_message += b" back at you"
# since the segments will be received at max length for a # since the segments will be received at max length for a
# StreamDataMessage, the appended text will end up in a # StreamDataMessage, the appended text will end up in a
# separate packet. # separate packet.
print("Sending " + str(len(message)) + " bytes, receiving " + str(len(expected_rx_message)) + " bytes, ") print(f"Sending {len(message)} bytes, receiving {len(expected_rx_message)} bytes, ")
buffer.write(message) buffer.write(message)
buffer.flush() buffer.flush()
@ -621,12 +621,12 @@ class TestLink(unittest.TestCase):
for unit in units: for unit in units:
if abs(num) < 1000.0: if abs(num) < 1000.0:
if unit == "": if unit == "":
return "%.0f %s%s" % (num, unit, suffix) return f"{num:.0f} {unit}{suffix}"
else: else:
return "%.2f %s%s" % (num, unit, suffix) return f"{num:.2f} {unit}{suffix}"
num /= 1000.0 num /= 1000.0
return "%.2f%s%s" % (num, last_unit, suffix) return f"{num:.2f}{last_unit}{suffix}"
if __name__ == '__main__': if __name__ == '__main__':
unittest.main(verbosity=1) unittest.main(verbosity=1)
@ -650,16 +650,16 @@ def targets(yp=False):
threads = yappi.get_thread_stats() threads = yappi.get_thread_stats()
for thread in threads: for thread in threads:
print( print(
"Function stats for (%s) (%d)" % (thread.name, thread.id) f"Function stats for ({thread.name}) ({int(thread.id)})"
) # it is the Thread.__class__.__name__ ) # it is the Thread.__class__.__name__
yappi.get_func_stats(ctx_id=thread.id).save("receiver_thread_"+str(thread.id)+".data", type="pstat") yappi.get_func_stats(ctx_id=thread.id).save(f"receiver_thread_{thread.id}.data", type="pstat")
except Exception as e: except Exception as e:
print("Error: "+str(e)) print(f"Error: {e}")
if hasattr(resource.link.attached_interface, "rxptime"): if hasattr(resource.link.attached_interface, "rxptime"):
rx_pr = (resource.link.attached_interface.rxb*8)/resource.link.attached_interface.rxptime rx_pr = (resource.link.attached_interface.rxb*8)/resource.link.attached_interface.rxptime
print("Average RX proccessing rate: "+size_str(rx_pr, "b")+"ps") print(f"Average RX proccessing rate: {size_str(rx_pr, 'b')}ps")
def link_established(link): def link_established(link):
print("Link established") print("Link established")
@ -670,7 +670,7 @@ def targets(yp=False):
def handle_message(message): def handle_message(message):
if isinstance(message, MessageTest): if isinstance(message, MessageTest):
message.data = message.data + " back" message.data = f"{message.data} back"
channel.send(message) channel.send(message)
channel.register_message_type(MessageTest) channel.register_message_type(MessageTest)
@ -685,17 +685,17 @@ def targets(yp=False):
buffer_read_len += len(data) buffer_read_len += len(data)
response_data.append(data) response_data.append(data)
if data == "Hi there".encode("utf-8"): if data == b"Hi there":
RNS.log("Sending response") RNS.log("Sending response")
for data in response_data: for data in response_data:
buffer.write(data + " back at you".encode("utf-8")) buffer.write(data + b" back at you")
buffer.flush() buffer.flush()
buffer_read_len = 0 buffer_read_len = 0
if buffer_read_len == BUFFER_TEST_TARGET: if buffer_read_len == BUFFER_TEST_TARGET:
RNS.log("Sending response") RNS.log("Sending response")
for data in response_data: for data in response_data:
buffer.write(data + " back at you".encode("utf-8")) buffer.write(data + b" back at you")
buffer.flush() buffer.flush()
buffer_read_len = 0 buffer_read_len = 0
@ -745,7 +745,7 @@ def resource_profiling():
resource_timeout = 120 resource_timeout = 120
resource_size = 5*1000*1000 resource_size = 5*1000*1000
data = os.urandom(resource_size) data = os.urandom(resource_size)
print("Sending "+size_str(resource_size)+" resource...") print(f"Sending {size_str(resource_size)} resource...")
import yappi import yappi
yappi.start() yappi.start()
@ -759,22 +759,22 @@ def resource_profiling():
time.sleep(0.01) time.sleep(0.01)
t = time.time() - start t = time.time() - start
print("Resource completed at "+size_str(resource_size/t, "b")+"ps") print(f"Resource completed at {size_str(resource_size / t, 'b')}ps")
yappi.get_func_stats().save("sender_main_calls.data", type="pstat") yappi.get_func_stats().save("sender_main_calls.data", type="pstat")
threads = yappi.get_thread_stats() threads = yappi.get_thread_stats()
for thread in threads: for thread in threads:
print( print(
"Function stats for (%s) (%d)" % (thread.name, thread.id) f"Function stats for ({thread.name}) ({int(thread.id)})"
) # it is the Thread.__class__.__name__ ) # it is the Thread.__class__.__name__
yappi.get_func_stats(ctx_id=thread.id).save("sender_thread_"+str(thread.id)+".data", type="pstat") yappi.get_func_stats(ctx_id=thread.id).save(f"sender_thread_{thread.id}.data", type="pstat")
# t_pstats = yappi.convert2pstats(tstats) # t_pstats = yappi.convert2pstats(tstats)
# t_pstats.save("resource_tstat.data", type="pstat") # t_pstats.save("resource_tstat.data", type="pstat")
if hasattr(resource.link.attached_interface, "rxptime"): if hasattr(resource.link.attached_interface, "rxptime"):
rx_pr = (resource.link.attached_interface.rxb*8)/resource.link.attached_interface.rxptime rx_pr = (resource.link.attached_interface.rxb*8)/resource.link.attached_interface.rxptime
print("Average RX proccessing rate: "+size_str(rx_pr, "b")+"ps") print(f"Average RX proccessing rate: {size_str(rx_pr, 'b')}ps")
l1.teardown() l1.teardown()
time.sleep(0.5) time.sleep(0.5)
@ -791,9 +791,9 @@ def size_str(num, suffix='B'):
for unit in units: for unit in units:
if abs(num) < 1000.0: if abs(num) < 1000.0:
if unit == "": if unit == "":
return "%.0f %s%s" % (num, unit, suffix) return f"{num:.0f} {unit}{suffix}"
else: else:
return "%.2f %s%s" % (num, unit, suffix) return f"{num:.2f} {unit}{suffix}"
num /= 1000.0 num /= 1000.0
return "%.2f%s%s" % (num, last_unit, suffix) return f"{num:.2f}{last_unit}{suffix}"