fluentpython / example-code-2e

Example code for Fluent Python, 2nd edition (O'Reilly 2022)
https://amzn.to/3J48u2J
MIT License
3.17k stars 902 forks source link

Implementation details in CH21 #16

Closed siuszy closed 3 years ago

siuszy commented 3 years ago

I deployed the local server according to SETTING UP TEST SERVERS, finding two small suspected errors both in slow_server.py.

The first one is about the parser in the main fucncion(Line 59). The sokcet module requires the parameter bind to be str, bytes, or bytearray , so I add a default value as the other part. (This problem is maybe linked to the python environment thing)

The second one is in the doGet function of SlowHTTPRequestHandler(Line41). I get an AttributeError when I first ran the ERROR option, which told me that the HTTPStatus did not have this attribute IM_A_TEAPOT, so maybe we could just throw a number(418 according to the book) instead?

ramalho commented 3 years ago

Thanks for reporting these issues.

The HTTPStatus.IM_A_TEAPOT was implemented in Python 3.9, which is the target version for all the examples in the book, unless otherwise noted. See: https://docs.python.org/3/library/http.html#http-status-codes

I am not sure I understood the other problem you reported, please clarify. Thanks!

siuszy commented 3 years ago

Thank you for your time and sorry for missing the version problem! My first problem is that the parser function doesn't add a default value to the bind parameter, ant it seems that the socket module would report a mistake like str,bytes or bytearray expected, not NoneType under my environment(Python3.7), the error is thrown out by socketserver.py in my library. So I don't know if it is appropriate to just add an empty string as the default value?

leorochael commented 3 years ago

Hi @siuszy, The bind cmdline argument in slow_server.py is passed into http.server.test(), from there into http.server._get_best_family, and finally into socket.getaddrinfo().

The documentation specifies that the host parameter from getaddrinfo accepts str, bytes or None.

And None is exactly what the value of args.bind will be if the --bind parameter is not specified and its definition has no default value, according to the definition of the parser.add_argument() found in the line 59 you pointed out.

The example runs fine when args.bind is None, outputing:

Serving HTTP on 0.0.0.0 port 8000 (http://0.0.0.0:8000/) ...

Which is exactly what is intended.

Can you please specify in detail what problem do you see exactly when the --bind argument has no default and is no passed in your environment?

siuszy commented 3 years ago

The document said that None is accepted, but the error message thrown by the socket.bind() showed that NoneType is not allowed. I thought maybe when the default value is None, the undergo procedure is somewhat blocked by my environment(Anaconda)?

ramalho commented 3 years ago

@siuszy we are unable to reproduce the problem you are having.

  1. What is exactly the Python version you are using (use python -V on the command line)?
  2. Please reproduce precisely the complete traceback that appears in your terminal, starting with the complete command line that you are using to run the slow_server.py script, and ending with the line with the final exception message.

Thank you.

leorochael commented 3 years ago

I managed to track down the problem to Python version 3.7:

$ python slow_server.py 
Traceback (most recent call last):
  File "slow_server.py", line 92, in <module>
    bind=args.bind,
  File "/usr/lib/python3.7/http/server.py", line 1230, in test
    with ServerClass(server_address, HandlerClass) as httpd:
  File "/usr/lib/python3.7/socketserver.py", line 452, in __init__
    self.server_bind()
  File "slow_server.py", line 85, in server_bind
    return super().server_bind()
  File "/usr/lib/python3.7/http/server.py", line 137, in server_bind
    socketserver.TCPServer.server_bind(self)
  File "/usr/lib/python3.7/socketserver.py", line 466, in server_bind
    self.socket.bind(self.server_address)
TypeError: str, bytes or bytearray expected, not NoneType

The default value for the bind= parameter of http.server.test in that version is the empty string instead of None.

leorochael commented 3 years ago

Adding a default='', to the argument parser configuration for the bind parameter fixes it for Python 3.7:

    parser.add_argument('--bind', '-b', metavar='ADDRESS', default='',
                        help='Specify alternate bind address '
                             '[default: all interfaces]')

But it breaks for more recent versions, which expects None as the default parameter, so it's not an acceptable solution.

A more complicated solution for stradling these versions would be to try to fetch the default bind= value from the http.server.test() function, or to try not to pass the bind= argument if the value is not given on the command line.

But I believe both these alternatives are out of scope for the code in the book, which should strive for simplicity.

siuszy commented 3 years ago

@ramalho @leorochael sorry for causing this trouble and haven't clarified the exact error! Last comment showed the exact trouble I met and further gave me the answer, thank you! I tried to locate the exact function that caused the trouble but I was totally lost (thought it was something in the socket module).