I will try not to repeat what has already been said:
Syntax Error
test.py:173: SyntaxWarning: invalid escape sequence '\ '
print("|" + content_lines[i][: width - 2].ljust(width - 9) + " /\ /\ |")
PEP-8 Compliance
Since you state that you are PEP-8 compliant, let me mention:
- All of your import statements should be at the top of your file. You have some intervening global variable definitions between groups of imports.
- You should have a blank line between import statements for Python's standard library imports and related third-party imports.
-
Write docstrings for all public modules, functions, classes, and methods. Docstrings are not necessary for non-public methods, but you should have a comment that describes what the method does. This comment should appear after the def line.
- I doubt that you consider all of the functions defined in your code to be public. Those that have been defined for private use should have names that begin with a leading underscore. Likewise for global variables. This will also limit what names will be imported if somebody does a
from your_module import *.
Better Help
You offer:
Enter command (X to exit, + to fetch URL, x to close tab, ? for help):
So I entered:
+ https://google.com
And 'Invalid command' appeared briefly before it disappeared. I had to look at the code to figure out that one has to enter '+' by itself and then a prompt for the URL will be made.
I also foolishly tried to exit by entering 'x' instead of 'X'. Could/should this command be case-insensitive? But looking at the code I see that you have separate logic for when 'x' is entered. But this is not described in your help. Would using a different letter for this command, whatever it is supposed to do, be more user-friendly?
Unable to Fetch Page Due to Missing User-Agent Header.
I received the following error when attempting to fetch a URL:
Not Acceptable!
An appropriate representation of the requested resource could not be found
on this server. This error was generated by Mod_Security.
This error was caused by not passing to requests.get a User-Agent header. You could use, for example:
headers = {'User-Agent': 'clippy/0.0.1'}
result = requests.get(url, headers=headers)
Pythonic and Efficient Code
Where you have a statement such as ...
elif command == "" or command == "0" or command == "\n" or command == " ":
... use instead:
elif command in ("", "0", "\n", " "):
You have:
def incomplete_brackets(lines):
merged_lines = []
buffer = ""
extra = []
print(lines.splitlines()[1])
time.sleep(0.1)
for i in range(len(lines.splitlines())):
line = lines.splitlines()[i]
if "[" in line and "]" not in line:
buffer = line
print(f"Found buffer {buffer}")
while "]" not in buffer:
i += 1
buffer += " "
buffer += lines.splitlines()[i]
print(f"buffer is now {buffer}")
extra.append(i)
merged_lines.append("\n")
merged_lines.append(buffer)
else:
merged_lines.append("\n")
merged_lines.append(line)
for a in extra:
print(merged_lines[(a * 2) + 1]) # *2 since newlines
merged_lines[(a * 2) + 1] = ""
return "".join(merged_lines)
The first thing I noticed is that you repeatedly calculate lines.splitlines() without the value of lines having changed. These are redundant and unnecessary calculations. Instead assign split_lines = lines.splitlines() prior to entering the for loop and reference split_lines wherever you have lines.splitlines().
In the same function you have:
...
while "]" not in buffer:
i += 1
...
buffer += lines.splitlines()[i]
...
So if you do not find the closing "]" bracket you look for it in subsequent lines. But what if there is no closing bracket? Won't i become an invalid index and cause lines.splitlines()[i] to raise an exception? Or am I missing something? If so, include a comments as to why this is correct.
You are also doing potentially a lot of string concatenations to variable buffer. Consider making buffer a list of strings to which you do a final ''.join(buffer) to create the concatenation of the strings.
Improved Timing
You currently have:
def timer():
global content
global elapsed_time
start = time.time()
elapsed_time = 0
while True:
# elapsed_time = int(time.time() - start)
# print(elapsed_time+1)
elapsed_time += 1
time.sleep(0.05)
Note that start is being assigned but not used (but it could be, see below).
This function, which runs in a separate thread, apparently is trying to increment elapsed_time by 1 every .05 seconds. Since it is competing with other threads for the CPU within the same or different processes, this will not be particularly accurate. You could do better with:
def timer():
global content
global elapsed_time
start = time.monotonic()
elapsed_time = 0
while True:
time.sleep(0.05)
elapsed_time = int((time.monotonic() - start) / .05)
Simplify main
You have a very large and complicated if/elif construct testing the various possible values for command. This could be simplified in a couple of ways:
- Take the logic for each condition and place it in a separate function.
- Additionally, replace the now-simplified
if/elif conditions with a dictionary whose keys are the valid commands and whose values are the functions specified in 1. Now it becomes a simple matter of checking to see if the entered command is in the dictionary and if so, calling the associated function to process the command.
So Many Global Variables!
Since Python is a language that supports object-oriented programming, consider how your implementation might profit by creating one or more classes whose methods and attributes replace your many functions and global variables. This would make the code more readable, maintainable and extensible.