classification
Title: In SocketServer, why not passing a factory instance for the RequestHandlerClass instead of the class itself?
Type: behavior Stage:
Components: Documentation, Library (Lib) Versions: Python 3.7, Python 3.6
process
Status: open Resolution:
Dependencies: Superseder:
Assigned To: docs@python Nosy List: docs@python, dominic108, eric.smith, martin.panter, r.david.murray
Priority: normal Keywords: patch

Created on 2017-03-30 12:08 by dominic108, last changed 2017-05-08 04:16 by louielu.

Files
File name Uploaded Description Edit
Issue29947_for_discussion_04.patch dominic108, 2017-04-05 15:13 review
Pull Requests
URL Status Linked Edit
PR 1496 open louielu, 2017-05-08 04:16
Messages (20)
msg290840 - (view) Author: Dominic Mayers (dominic108) * Date: 2017-03-30 12:08
I am just curious to know if someone considered the idea of passing a factory instance that returns RequestHandlerClass instances instead of directly passing the class? It may affect existing handlers that read non local variables, but there should be a way to make the factory optional. The purpose is only aesthetic and a better organization of the code. I find it awkward to have to subclass the server every time that we have an handler that needs special objects, a database connection, a socket connection to another party, etc. The server class should have a single purpose: accept a request and pass it to an handler. We should only need to subclass a server when we need to do that in a different way : TCP vs UDP, Unix Vs INET, etc. The usage is simpler and more natural. Instead of subclassing the server, we create a factory for the handler.
msg290855 - (view) Author: Eric V. Smith (eric.smith) * (Python committer) Date: 2017-03-30 16:03
Just a guess, but it's because this code was written at a time when subclassing was the preferred way to extend code. Now we know better :), but it's not possible to change history, unfortunately. Lots of code depends on the current behavior.

If you find a way to make a factory function work, while preserving backward comparability, then we can re-open this issue and consider the enhancement request then.
msg290886 - (view) Author: Dominic Mayers (dominic108) * Date: 2017-03-31 01:46
One way to make the factory optional is to offer a MixIn. I attached a file with a FactoryMixIn. It's just that I find it awkward that the proposed approach is to pass the extra parameters in a subclassed server. More modern approaches should also be offered.
msg290887 - (view) Author: Dominic Mayers (dominic108) * Date: 2017-03-31 02:20
Finally, I looked around and people just use the server to pass any extra parameter. I do find it awkward, but it works and it is simple, in fact simpler than having to define a factory object. I don't close it, because I will be happy to see another opinion. I would use this factory approach for myself, because I am just not comfortable to add an attribute to a server that would not even look at it, but maybe that it just me.
msg290892 - (view) Author: Dominic Mayers (dominic108) * Date: 2017-03-31 04:33
On the other hand, it occurs to me that this seems way more flexible than passing the object through the server, because you share the factory with the server, not only the object. This means that you could even change the type of the handler while the server is running !
msg290952 - (view) Author: R. David Murray (r.david.murray) * (Python committer) Date: 2017-04-01 02:06
I think I'm missing something here.  What prevents one from passing a factory function as the RequestHandlerClass argument?  In Python, a class name *is* a factory function for class instances.
msg290963 - (view) Author: Dominic Mayers (dominic108) * Date: 2017-04-01 05:40
I am a bit ashamed that I missed that. Still, the intent in the current code, the name of the parameter, the examples, etc. is that we pass the handler class. This is more than its __init__ function and less than a generic factory  method. An important difference, which could become important, is that with a factory the handler type could depend on the request.  As pointed out by Eric, passing the class was perhaps the intent in the early days. Now, perhaps many use it differently and pass a factory method, not a class, but it still appears as a hack that does not respect the intent. One could legitimately worry that this hack will not be supported in future versions, because it is not documented.
msg290976 - (view) Author: Dominic Mayers (dominic108) * Date: 2017-04-01 09:42
Perhaps I should raise a separate issue, but it is related, because the current code "requires" that we define an handler class with `setup()`, `handle()` and `finish()` in its API. If you look at the actual code, there is no such requirement. We only have to pass a factory method that receives three arguments, creates a new handler instance and starts it. The handler does not have to offer any API - you don't even have to subclass the RequestHandlerBase class. I still say that it "requires" it, because it is part of the documentation and, if you don't, you could be in trouble in future versions. However, this requirement is clearly annoying, because if you have an handler for another server, you need to re-factorize the code to provide this API, which is not even used. I am sure there is no such requirement, but it's not clear at first. 

This is clearly not just a documentation issue. The intent in the code is the concern of the designers of the code. David and I assume that the intent is that we can provide any arbitrary factory method that receives the three arguments, creates and starts the handler, but we can be mistaken. In fact, to me this looks as a hack, especially given the parameter name, which says that we must pass the handler class. An handler class is indeed a factory method, but it is a very specific one, not the one that we want to pass if we must include extra parameters.
msg290996 - (view) Author: Dominic Mayers (dominic108) * Date: 2017-04-01 16:16
To sum up, David clarified that we can in fact easily pass an arbitrary factory method that creates and starts a request handler, instead of a request handler class with setup, handle and finish in its API. This could indeed be a valid reason to consider this issue resolved,  but I would like to be sure that it is really a normal and supported use of the code. The only place where I can ask this is here. If I ask in StackOverflow, etc. I would get all kind of opinions  and it would not be useful.  To be honest, looking at the parameter name, the documentation,  the examples, etc. it does not look at all a normal and supported use of the code, but who am I to decide that?  If it turns out to be a normal and supported use of the code, then I will create a different issue only about the documentation, because it's not clear at all.
msg291002 - (view) Author: R. David Murray (r.david.murray) * (Python committer) Date: 2017-04-01 20:40
Well, we could document it as a factory argument, and explain that the argument name is an historical artifact.
msg291004 - (view) Author: R. David Murray (r.david.murray) * (Python committer) Date: 2017-04-01 20:54
Given how old socket server is, and that it doesn't actually require that API, we should probably just reword the documentation so that it is clear that RequestHandlerClass is the default Handler implementation (and that you can work with setup/handle/finish if you want to subclass it), and document what the *actual* requirements on the Handler are.  

We are unlikely to change socketserver's actual API at this point.

I don't think there's any need for a new issue, let's just make this a documentation issue.  The original title still even applies :)  We will need signoff from someone with more direct interest in the socketserver module than me, but given that the "future" for the stuff socketserver handles is asyncio, I suspect the only objection we might get is "it's not worth it".  But if someone wants to do the work, *I* don't see a reason to reject it.
msg291005 - (view) Author: Dominic Mayers (dominic108) * Date: 2017-04-01 22:16
The reason why I feel we should not make it immediately a documentation issue is that I don't know how a person working on documentation could proceed ahead without a clear go ahead signal from developers. In that sense,  having a documentation that says that the variable name does not reflect its intent seems tricky. It's very easy to change a variable name. It cannot possibly break the code, except for a collision, but they are easy to avoid.  If we don't have the interest of developers, not even for a simple renaming, I don't see a go ahead signal in this.
msg291006 - (view) Author: Dominic Mayers (dominic108) * Date: 2017-04-01 22:34
Oops, I did not realize that David was one of the developers. Well, may be this needs the attention of more than one developer.
msg291024 - (view) Author: Martin Panter (martin.panter) * (Python committer) Date: 2017-04-02 05:44
By “factory instance”, I presume you just mean a function (or class or method) that returns an appropriate object when called. (I think these are normally called “factory functions”. Instances are objects, the things that class constructors and factories return.)

What is being proposed? The best idea that I understand is hinted in <https://bugs.python.org/issue29947#msg290976>. That is, to document and allow any function (not just a class or factory) to be passed as (or instead of) the RequestHandlerClass parameter.

This is a significant change to the documentation and API, but I would support it, and I think it should already be supported by the implementation.

The question of renaming the RequestHandlerClass parameter is a harder decision IMO. I think it would be easier to make the documentation clear that it is a misnomer and does not have to be a class. But in the long term, renaming the parameter seems nicer.

If it was “renamed”, we would first have to add a competing parameter, keep the old parameter around, consider adding deprecation warnings in 2020 when Python 2 is dead, document both parameters, decide if anything should happen when both parameters are passed, etc. Much trickier, but possible. This renaming process is similar to “base64.encodestring” vs “encodebytes”, but more involved. I can’t think of an example where a function or constructor parameter was renamed like this.
msg291026 - (view) Author: Dominic Mayers (dominic108) * Date: 2017-04-02 06:41
I did not think very far when said that renaming the parameter could not possibly break the code ! Oh well ! But, renaming the parameter was not important in itself.  It was to make the situation clearer and easier for those who write the documentation. Martin mentioned that it is a big change for the API. This is what I had in mind. And yes, I should have used 'factory function', not 'factory instance'. Oh well ! I am glad things are working and that we will move ahead to resolve this issue.
msg291036 - (view) Author: R. David Murray (r.david.murray) * (Python committer) Date: 2017-04-02 13:34
Yes, the difficulty in renaming the parameter was why I suggested a doc change only.  I'm not sure it it is worth it to go through a deprecation cycle for socketserver to change the name, though it certainly would be nice.  Martin and I could make that decision, but it would be better to get input from other devs.  And, if we make this the documentation issue, we should open a separate issue for the parameter rename.
msg291048 - (view) Author: Dominic Mayers (dominic108) * Date: 2017-04-03 02:10
I started to look at the documentation to see what would need to be changed, assuming that we agree for a change in the API. Just for the purpose of this discussion, I created a patch that only change the comments in socketserver.py.
msg291056 - (view) Author: Dominic Mayers (dominic108) * Date: 2017-04-03 08:09
The key point, IMHO, is that the BaseRequestHandler class is just provided as an option and its API (setup, handle and finish) is ignored by the code that we support. 

Some applications may have used the API, but these are details in applications. Simply, we keep BaseRequestHandler as it is so that we do not break these applications. 

So, yes, we should keep supporting the API, but we do not expect this API on our side. The latter is the key point.
msg291142 - (view) Author: Dominic Mayers (dominic108) * Date: 2017-04-04 21:43
I simplified the patch. Using a class as a factory function is the simplest case, so I took advantage of this. I also give one way to pass extra parameters to the handler in the factory function, because it's the main reason why we cannot always use a class.
msg291181 - (view) Author: Dominic Mayers (dominic108) * Date: 2017-04-05 15:13
An improved version of the patch, I hope. I will remove the old patch, because it's really does not help to see the old versions.
History
Date User Action Args
2017-05-08 04:16:49louielusetpull_requests: + pull_request1598
2017-04-05 15:13:24dominic108setfiles: - Issue29947_for_discussion_03.patch
2017-04-05 15:13:01dominic108setfiles: + Issue29947_for_discussion_04.patch

messages: + msg291181
2017-04-04 22:41:29dominic108setfiles: + Issue29947_for_discussion_03.patch
2017-04-04 22:41:14dominic108setfiles: - Issue29947_for_discussion_02.patch
2017-04-04 21:45:15dominic108setfiles: - Issue29947_for_discussion.patch
2017-04-04 21:43:17dominic108setfiles: + Issue29947_for_discussion_02.patch

messages: + msg291142
2017-04-03 08:09:30dominic108setmessages: + msg291056
2017-04-03 02:10:35dominic108setmessages: + msg291048
2017-04-03 02:07:52dominic108setfiles: + Issue29947_for_discussion.patch
keywords: + patch
2017-04-03 02:06:58dominic108setfiles: - factorymixinclass
2017-04-02 13:34:53r.david.murraysetmessages: + msg291036
2017-04-02 06:41:14dominic108setmessages: + msg291026
2017-04-02 05:44:07martin.pantersetmessages: + msg291024
2017-04-01 22:34:22dominic108setmessages: + msg291006
2017-04-01 22:16:24dominic108setmessages: + msg291005
2017-04-01 20:54:21r.david.murraysetassignee: docs@python
type: enhancement -> behavior
components: + Documentation
versions: + Python 3.6
nosy: + docs@python

messages: + msg291004
stage: resolved ->
2017-04-01 20:40:04r.david.murraysetmessages: + msg291002
2017-04-01 16:16:37dominic108setnosy: + martin.panter
messages: + msg290996
2017-04-01 09:42:56dominic108setmessages: + msg290976
2017-04-01 05:40:18dominic108setmessages: + msg290963
2017-04-01 02:06:25r.david.murraysetnosy: + r.david.murray
messages: + msg290952
2017-03-31 04:33:39dominic108setmessages: + msg290892
2017-03-31 02:21:00dominic108setstatus: closed -> open
resolution: wont fix ->
2017-03-31 02:20:39dominic108setstatus: open -> closed
resolution: wont fix
messages: + msg290887
2017-03-31 01:46:53dominic108setstatus: closed -> open
files: + factorymixinclass
resolution: rejected -> (no value)
messages: + msg290886
2017-03-30 16:03:58eric.smithsetstatus: open -> closed

versions: - Python 2.7, Python 3.3, Python 3.4, Python 3.5, Python 3.6
nosy: + eric.smith

messages: + msg290855
resolution: rejected
stage: resolved
2017-03-30 12:08:53dominic108create