1. Annotate the following python expressions with the specific types of each literal, as well as the overall return value:
```
i = 3 # the literal "3" and the overall return "i" are both integer as `int`
f = 3.0 # the literal "3.0" and the overal return "f" are both floating-point literal of type `float`
c = 3+3i # the literal "3" is an integer as `int` while the literal "3i" is a complex literal with 0 as the real part and the 3i as the imaginary part; the overal return c is a complex number of type `complex`
b = b'abcdefg' # the literal "b'abcdefg'" and the overall return "b" are both bytes literal of type `bytes`
s = 'abcdefg' # the literal "'abcdegf'" and the overall return "s" are both string literal of type `str`
s = "abcdefg" # the literal ""abcdegf"" and the overall return "s" are both string literal of type `str`
```
❓ I have a question for the function 'c' as it gives a syntax error when I enter it in Python saying invalid decimal literal, so how can we enter such kind of complex number of type complex in python?
2. For each of the types string, bytestring, integer, floating point, and imaginary literals, construct a 5x5 table showing what happens when the character "0" or number 0 is compared to each other type.
# Define literals
str_literal = "12345"
bytes_literal = b"12345"
int_literal = 12345
float_literal = 123.45
imag_literal = 123+45j
# Define comparison function
def compare(literal):
# Create table header
print(f"\nComparison table for {type(literal).__name__} literals:\n")
print(f"{'':10} {'0':^5} {literal[0]:^5} {literal[1]:^5} {literal[2]:^5} {literal[3]:^5} {literal[4]:^5}")
# Compare each element in the literal to 0
for i, elem in enumerate(literal):
is_equal = "==" if elem == 0 else "!="
print(f"{elem:<10} {is_equal:^5} {0:^5} {is_equal:^5} {i:^5} {is_equal:^5} {i+1:^5} {is_equal:^5} {i+2:^5} {is_equal:^5} {i+3:^5} {is_equal:^5} {i+4:^5}")
# Call comparison function for each type of literal
compare(str_literal)
compare(bytes_literal)
compare(int_literal)
compare(float_literal)
compare(imag_literal)
Comparison table for str literals:
0 1 2 3 4
1 != == != != !=
2 != != == != !=
3 != != != == !=
4 != != != != ==
5 != != != != !=
Comparison table for bytes literals:
0 1 2 3 4
49 != == != != !=
50 != != == != !=
51 != != != == !=
52 != != != != ==
53 != != != != !=
Comparison table for int literals:
0 1 2 3 4
12345 != != != != !=
Comparison table for float literals:
0 1 2 3 4
123.45 != != != != !=
Comparison table for complex literals:
0 1 2 3 4
(123+45j) != != != != !=
3. Now do the same thing for the + and == operators for each pair of types in two other 5x5 tables.
4. Write a small program that uses the AST module to open the lexer/parser example, and dumps the resulting AST. See if there are any arguments to the parse method which make the output easier to read.
Maybe we can first write a simple lexer sample, and then import astor module to output the AST in a more human-readable format.
import ply.lex as lex
# Define the list of token names
tokens = (
'NUMBER',
'PLUS',
'MINUS'
)
# Define the regular expressions for each token
t_PLUS = r'\+'
t_MINUS = r'-'
t_NUMBER = r'\d+'
# Define a rule to handle whitespace
def t_whitespace(t):
r'\s+'
pass
# Define a rule to handle errors
def t_error(t):
print("Invalid character '%s'" % t.value[0])
t.lexer.skip(1)
# Create the lexer
lexer = lex.lex()
# Test the lexer on a sample input
input_str = "3 + 4 * (2 - 1)"
lexer.input(input_str)
for token in lexer:
print(token.type, token.value)
import ast
import astor
tree = ast.parse(input_str)
print(astor.to_source(tree))
5. What does this do? print("\007") Can you figure out why?
It prints an ASCII bell character to the console, and it is used to produce an audible signal. The character is interpreted by the computer's terminal or console to produce a beep or flashing effect, typically used to alert something.
6. What does this do? print("abcdefg\rhijklmnop") Can you figure out why?
The \r character in the string is used to instructs the console to return the cursor to the beginning of the current line without advancing to the next line, so in this case, it will prints the string "hijklmnop" over the string "abcdefg" in the console, replacing the first seven characters of the first string with the second string.
>>> print("abcdefg\rhijklmnop")
hijklmnop
7. This is an HTTP server. Run it and connect to http://localhost:8000 . Why doesn't this do the same thing as what you see above? (don't forget to shut down the HTTP server afterward.)
```
import http.server
import socketserver
from urllib.parse import urlparse, parse_qs
class MyRequestHandler(http.server.SimpleHTTPRequestHandler):
def do_GET(self):
self.send_response(200)
self.send_header('Content-type', 'text/plain')
self.end_headers()
output_string = "abcdefg\rhijklmnop"
self.wfile.write(output_string.encode())
PORT = 8000
Handler = MyRequestHandler
httpd = socketserver.TCPServer(("", PORT), Handler)
print(f"Serving on port {PORT}")
httpd.serve_forever()
```
The reason why the HTTP server code above does not do the same thing as what was seen before is because the MyRequestHandler class's do_GET method sends a response with a text/plain content type, and the response body is set to "abcdefg\rhijklmnop". When the browser requests the URL http://localhost:8000, the HTTP server sends the response with the content type text/plain, and the response body containing the string "abcdefg\rhijklmnop". The browser then displays this response as plain text, and interprets the \r character as a carriage return, which causes the browser to display only "hijklmnop".
8. Think of a way someone might try to commit a crime or hide an embezzlement scheme using the answers to the above two questions. Hint: applications usually output on web pages, but developers and system administrators usually troubleshoot in the console. One way someone might try to commit a crime or hide an embezzlement scheme is by manipulating the output that appears in the browser vs. what appear in the console. For instance, a malicious developer could modify the code of a web application to include a string with a \r character followed by some text that would overwrite the previously displayed text in the browser. This could be used to conceal some illegal activity or transactions that are being carried out through the web application.
9. Think of how you would do input validation to prevent such a crime. Replace the code REPLACE ME here so the tests pass:
def validate(str):
for char in str:
if REPLACE ME:
raise ValueError("Crime detected!")
try:
validate("abcdefg")
print("✅ test1 pass!")
except:
print("❌ test1 pass!")
try:
validate("abcdefg\rhijklmnop")
print("❌ test2 fail!")
except ValueError as err:
print("✅ test2 pass!")
To prevent such a crime, maybe we can validate the input string to ensure that it does not contain any malicious characters such as \r.
def validate(input_str):
illegal_chars = ["\r"]
for char in input_str:
if char in illegal_chars:
raise ValueError("Crime detected!")
try:
validate("abcdefg")
print("✅ test1 pass!")
except:
print("❌ test1 fail!")
try:
validate("abcdefg\rhijklmnop")
print("❌ test2 fail!")
except ValueError as err:
print("✅ test2 pass!")