-
-
Notifications
You must be signed in to change notification settings - Fork 13
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: Add translated
imap-codec
Python examples
This adds Python examples `parse_command`, `parse_greeting` and `parse_response`, which are translated from the Rust examples available for `imap-codec`.
- Loading branch information
1 parent
5b60b23
commit ec57ca7
Showing
4 changed files
with
194 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
import sys | ||
from enum import Enum | ||
from typing import Optional | ||
|
||
COLOR_SERVER = "\x1b[34m" | ||
COLOR_CLIENT = "\x1b[31m" | ||
RESET = "\x1b[0m" | ||
|
||
|
||
class Role(Enum): | ||
Client = 1 | ||
Server = 2 | ||
|
||
|
||
def read_more(buffer: bytearray, role: Role): | ||
if not buffer: | ||
prompt = "C: " if role == Role.Client else "S: " | ||
else: | ||
prompt = ".. " | ||
|
||
line = read_line(prompt, role) | ||
|
||
# If `read_line` returns `None`, standard input has been closed. | ||
if line is None or line.strip() == "exit": | ||
print("Exiting.") | ||
sys.exit(0) | ||
|
||
buffer += line.encode() | ||
|
||
|
||
def read_line(prompt: str, role: Role) -> Optional[str]: | ||
color = COLOR_CLIENT if role == Role.Client else COLOR_SERVER | ||
|
||
try: | ||
line = input(f"{prompt}{color}") | ||
except EOFError: | ||
# Standard input has been closed. | ||
return None | ||
finally: | ||
print(RESET, end=None) | ||
|
||
return line + "\r\n" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
from common import COLOR_SERVER, RESET, Role, read_more | ||
from imap_codec import CommandCodec, DecodeFailed, DecodeIncomplete, DecodeLiteralFound | ||
|
||
WELCOME = r"""# Parsing of IMAP commands | ||
"C:" denotes the client, | ||
"S:" denotes the server, and | ||
".." denotes the continuation of an (incomplete) command, e.g., due to the use of an IMAP literal. | ||
Note: "\n" will be automatically replaced by "\r\n". | ||
-------------------------------------------------------------------------------------------------- | ||
Enter IMAP command (or "exit"). | ||
""" | ||
|
||
|
||
def main(): | ||
print(WELCOME) | ||
|
||
buffer = bytearray() | ||
|
||
while True: | ||
# Try to parse the first command in `buffer`. | ||
try: | ||
remaining, command = CommandCodec.decode(bytes(buffer)) | ||
# Parser succeeded. | ||
# Do something with the command ... | ||
print(command) | ||
# ... and proceed with the remaining data. | ||
buffer = bytearray(remaining) | ||
except DecodeIncomplete: | ||
# Parser needs more data. | ||
# Read more data. | ||
read_more(buffer, Role.Client) | ||
except DecodeLiteralFound: | ||
# Parser needs more data, and a command continuation request is expected. | ||
# Simulate literal acknowledgement ... | ||
print(f"S: {COLOR_SERVER}+ {RESET}") | ||
|
||
# ... and read more data. | ||
read_more(buffer, Role.Client) | ||
except DecodeFailed: | ||
# Parser failed. | ||
print("Error parsing command.") | ||
print("Clearing buffer.") | ||
|
||
# Clear the buffer and proceed with loop. | ||
buffer.clear() | ||
|
||
|
||
if __name__ == "__main__": | ||
main() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
from common import Role, read_more | ||
from imap_codec import DecodeFailed, DecodeIncomplete, GreetingCodec | ||
|
||
WELCOME = r"""# Parsing of IMAP greetings | ||
"S:" denotes the server. | ||
Note: "\n" will be automatically replaced by "\r\n". | ||
-------------------------------------------------------------------------------------------------- | ||
Enter IMAP greeting (or "exit"). | ||
""" | ||
|
||
|
||
def main(): | ||
print(WELCOME) | ||
|
||
buffer = bytearray() | ||
|
||
while True: | ||
# Try to parse the first greeting in `buffer`. | ||
try: | ||
remaining, greeting = GreetingCodec.decode(bytes(buffer)) | ||
# Parser succeeded. | ||
# Do something with the greeting ... | ||
print(greeting) | ||
# ... and proceed with the remaining data. | ||
buffer = bytearray(remaining) | ||
except DecodeIncomplete: | ||
# Parser needs more data. | ||
# Read more data. | ||
read_more(buffer, Role.Server) | ||
except DecodeFailed: | ||
# Parser failed. | ||
print("Error parsing greeting.") | ||
print("Clearing buffer.") | ||
|
||
# Clear the buffer and proceed with loop. | ||
buffer.clear() | ||
|
||
|
||
if __name__ == "__main__": | ||
main() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
from common import Role, read_more | ||
from imap_codec import DecodeFailed, DecodeIncomplete, DecodeLiteralFound, ResponseCodec | ||
|
||
WELCOME = r"""# Parsing of IMAP responses | ||
"S:" denotes the server, and | ||
".." denotes the continuation of an (incomplete) response, e.g., due to the use of an IMAP literal. | ||
Note: "\n" will be automatically replaced by "\r\n". | ||
-------------------------------------------------------------------------------------------------- | ||
Enter IMAP response (or "exit"). | ||
""" | ||
|
||
|
||
def main(): | ||
print(WELCOME) | ||
|
||
buffer = bytearray() | ||
|
||
while True: | ||
# Try to parse the first response in `buffer`. | ||
try: | ||
remaining, response = ResponseCodec.decode(bytes(buffer)) | ||
# Parser succeeded. | ||
# Do something with the response ... | ||
print(response) | ||
# ... and proceed with the remaining data. | ||
buffer = bytearray(remaining) | ||
except DecodeIncomplete: | ||
# Parser needs more data. | ||
# Read more data. | ||
read_more(buffer, Role.Server) | ||
except DecodeLiteralFound: | ||
# Parser needs more data. | ||
# | ||
# A client MUST receive any literal and can't reject it. However, if the literal is too | ||
# large, the client would have the (semi-optimal) option to still *read it* but discard | ||
# the data chunk by chunk. It could also close the connection. This is why we have this | ||
# option. | ||
# | ||
# Read more data. | ||
read_more(buffer, Role.Server) | ||
except DecodeFailed: | ||
# Parser failed. | ||
print("Error parsing response.") | ||
print("Clearing buffer.") | ||
|
||
# Clear the buffer and proceed with loop. | ||
buffer.clear() | ||
|
||
|
||
if __name__ == "__main__": | ||
main() |