These are chat archives for cherrypy/cherrypy

14th
Nov 2018
Giancarlos
@Giancarlos
Nov 14 2018 12:13
is there a different way to specify the config for a class in the class init or similar? I'm trying to define a class as a method dispatcher but the way I rewrote my routes is a little different from the examples, I instantiate my root and it has all the child paths as properties, it works nicely, but now I'm unsure of how to define some as a method dispatcher
Jason R. Coombs
@jaraco
Nov 14 2018 13:09
@Giancarlos Generally speaking, you can’t change dispatchers during a request, meaning you can’t rely on the default dispatcher for a portion of the handler dispatch (tracing a path to properties) and then the method dispatcher for methods on the class. The only place a dispatcher is selected is at the mounted path.
And I’ve had the exact same experience as you - wanting the default dispatch for the general traversal, but then the method dispatch at the final class.
What I ended up building was a bit of a hack, but it allowed me to have an approximation for that behavior.
I’d have already contributed that back to the cherrypy project except that it has some caveats, namely that it takes over for the dispatcher at the first decorated method, so any tools or other CherryPy constructs stop working on the method-selected methods.
Jason R. Coombs
@jaraco
Nov 14 2018 13:15
What I’d like to do is adapt that concept to (a) simply augment the class’ methods and (b) add first-class support in the default dispatcher for dispatching by method.
Jason R. Coombs
@jaraco
Nov 14 2018 13:22
I’ve opened #1757 to describe that desire/intention.
Giancarlos
@Giancarlos
Nov 14 2018 13:55
@jaraco thanks for that I'll look into it
Actually... I really love what you did there, I sorta wish CherryPy did support that out of the box, hopefully I can reuse that code thanks again!
Sviatoslav Sydorenko
@webknjaz
Nov 14 2018 14:03
@Giancarlos @jaraco not sure what you're trying to do, but here's more inspiration:
I'm cooking a GitHub App (bot integration) and for me I simply hijack the path with the event name https://github.com/webknjaz/ansiwatch-bot/blob/master/ansiwatch_bot/request_dispatcher.py
This hack allows me to have a class with methods named after GH events. And I also auto-unpack incoming JSON body as handler args :)
Giancarlos
@Giancarlos
Nov 14 2018 14:06
I'm mostly glad I'm not the only one doing my paths this way, once I realized I could do it it felt so much cleaner and Pythonic (at least OO'd)
his approach fits exactly what I'm working on for now at least
Sviatoslav Sydorenko
@webknjaz
Nov 14 2018 14:06
cool :)
I wasn't sure what is the end purpose though
Giancarlos
@Giancarlos
Nov 14 2018 14:07
instead of having 50 different tree mount statements with the config (where necessary) I can declare one class in the tree mount, and all that classes properties are the rest of the paths of my application (aka other class instances)
but I was trying to figure out how to set the config for one of those class instances after the fact
Sviatoslav Sydorenko
@webknjaz
Nov 14 2018 14:08
oh
Giancarlos
@Giancarlos
Nov 14 2018 14:08
because I want one of my classes to be a MethodDispatcher
Sviatoslav Sydorenko
@webknjaz
Nov 14 2018 14:08
I see
Giancarlos
@Giancarlos
Nov 14 2018 14:08
it makes the code paths look cleaner than repeating the same tree mount line over and over
Sviatoslav Sydorenko
@webknjaz
Nov 14 2018 14:09
we probably need to have official support for dispatcher composition...
Giancarlos
@Giancarlos
Nov 14 2018 14:09
but I hadn't realized what I'd be potentially sacrificing as a result :) thankfully @jaraco has done the same before
yeah outside of setting the config on the tree mount I'm not sure of another way of specifying a class as a method dispatcher
unless I can specify it somehow as a class property or something else, but I'll try what he suggested, it allows me to now have to undo the rework I did for my application routes
Giancarlos
@Giancarlos
Nov 14 2018 14:46
@jaraco sadly I'm seeing: TypeError: 'function' object is not iterable
Sviatoslav Sydorenko
@webknjaz
Nov 14 2018 14:47
full trace plz
Giancarlos
@Giancarlos
Nov 14 2018 14:47
500 Internal Server Error
The server encountered an unexpected condition which prevented it from fulfilling the request.

Traceback (most recent call last):
  File "/opt/diip_web/venv/local/lib/python2.7/site-packages/cherrypy/_cprequest.py", line 631, in respond
    self._do_respond(path_info)
  File "/opt/diip_web/venv/local/lib/python2.7/site-packages/cherrypy/_cprequest.py", line 662, in _do_respond
    self.get_resource(path_info)
  File "/opt/diip_web/venv/local/lib/python2.7/site-packages/cherrypy/_cprequest.py", line 758, in get_resource
    dispatch(path)
  File "/opt/diip_web/venv/local/lib/python2.7/site-packages/cherrypy/_cpdispatch.py", line 296, in __call__
    func, vpath = self.find_handler(path_info)
  File "/opt/diip_web/venv/local/lib/python2.7/site-packages/cherrypy/_cpdispatch.py", line 387, in find_handler
    nodeconf.update(node._cp_config)
TypeError: 'function' object is not iterable
Powered by CherryPy 15.0.0
Sviatoslav Sydorenko
@webknjaz
Nov 14 2018 14:56
you probably need to add bound._cp_config = True line in __get__ method
Giancarlos
@Giancarlos
Nov 14 2018 14:57
thanks I will try that
sadly not much changed
Sviatoslav Sydorenko
@webknjaz
Nov 14 2018 15:00
could you please come up with a minimum reproducible example and post a full source?
Giancarlos
@Giancarlos
Nov 14 2018 15:00
yes I can do that
Sviatoslav Sydorenko
@webknjaz
Nov 14 2018 15:00
:+1:
Giancarlos
@Giancarlos
Nov 14 2018 15:01
is it fine if it's one of those "all in one file" type of code snippets?
there, it gave me the same error when I ran it locally: Python 2.7 and CherryPy 15.0.0 under a virtualenvironment
Sviatoslav Sydorenko
@webknjaz
Nov 14 2018 16:27
oh
sorry
I meant s/True/{}/
Giancarlos
@Giancarlos
Nov 14 2018 16:28
it's ok, not sure I follow your last message though
Giancarlos
@Giancarlos
Nov 14 2018 16:29
oh!
Sviatoslav Sydorenko
@webknjaz
Nov 14 2018 16:29
I gave you wrong advice. I meant bound._cp_config = {}
Giancarlos
@Giancarlos
Nov 14 2018 16:30
that's fine, I greatly appreciate the help :D
Giancarlos
@Giancarlos
Nov 14 2018 17:01
darn sadly it's still giving me the same trace
on both that simplified sample and my rendition of it
Sviatoslav Sydorenko
@webknjaz
Nov 14 2018 17:02
oh
gist version or what I posted above?
Giancarlos
@Giancarlos
Nov 14 2018 17:03
the gist version with your changes applied
Sviatoslav Sydorenko
@webknjaz
Nov 14 2018 17:08
oh, i only tested that under python 3
Giancarlos
@Giancarlos
Nov 14 2018 17:08
yeah I was afraid of that, sadly I can't use Python 3 for this project (yet)
Giancarlos
@Giancarlos
Nov 14 2018 17:29
my Py2 vs Py3 fu is weak, I was trying to google earlier to see if there's an obvious answer to that
Sviatoslav Sydorenko
@webknjaz
Nov 14 2018 17:29
it's probably because of difference with bound vs unbound methods
Giancarlos
@Giancarlos
Nov 14 2018 17:29
oh
Sviatoslav Sydorenko
@webknjaz
Nov 14 2018 17:32
Giancarlos
@Giancarlos
Nov 14 2018 17:42
might have to look at that one later, just gonna work around this for now till it's my last task, decorators are something I don't do enough of sadly
Sviatoslav Sydorenko
@webknjaz
Nov 14 2018 17:50
ok.. it's some difference in descriptor protocol
__get__ isn't called under py2
gotcha!
it's difference between old-style and new-style classes
We started using technique of defaulting to new-style classes everywhere
@jaraco probably forgot to add a boilerplate line for this to work
Sviatoslav Sydorenko
@webknjaz
Nov 14 2018 17:57

https://docs.python.org/2/howto/descriptor.html#definition-and-introduction:

Note that descriptors are only invoked for new style objects or classes (a class is new style if it inherits from object or type).

The important points to remember are:

descriptors are invoked by the __getattribute__() method
overriding __getattribute__() prevents automatic descriptor calls
__getattribute__() is only available with new style classes and objects
object.__getattribute__() and type.__getattribute__() make different calls to __get__().
data descriptors always override instance dictionaries.
non-data descriptors may be overridden by instance dictionaries.

Jason R. Coombs
@jaraco
Nov 14 2018 17:58
I’ve used it on Python 3 only, so should have mentioned that earlier. Thanks @webknjaz
Giancarlos
@Giancarlos
Nov 14 2018 18:15
oh!
it's working with the metaclass change, let me confirm post works too
yeah! this is awesome, thanks for the help it works with post, and anything else is hitting the "default" method
which is as expected
Giancarlos
@Giancarlos
Nov 14 2018 18:29
thanks again @jaraco and @webknjaz that worked out cleanly
Sviatoslav Sydorenko
@webknjaz
Nov 14 2018 18:29
:+1:
Giancarlos
@Giancarlos
Nov 14 2018 18:29
my other solution woulda been to get the method type and pass off the handling to helper methods
Giancarlos
@Giancarlos
Nov 14 2018 18:42
hey @webknjaz how do you handle arguments? or did I run into another python 2 issue
whoops wrong tag
@jaraco ^
Sviatoslav Sydorenko
@webknjaz
Nov 14 2018 18:42
hey
I'm also a maintainer :)
be more verbose about your problem plz
Giancarlos
@Giancarlos
Nov 14 2018 18:43
yeah I have seen your name in the commits and source, just figured he might of dealt with arguments
so say I wanna define specific parameters for POST and GET
Sviatoslav Sydorenko
@webknjaz
Nov 14 2018 18:43
method args
Giancarlos
@Giancarlos
Nov 14 2018 18:43
class People(Object):

    @SelectedMethod
    def index(self, **kwargs):
        return "Default Index"

    @index.GET
    def get(self, boop, beep):
        return "GET {}{}".format(boop, beep)
TypeError: get() got an unexpected keyword argument 'boop'
Sviatoslav Sydorenko
@webknjaz
Nov 14 2018 18:46
works for me
Giancarlos
@Giancarlos
Nov 14 2018 18:46
so it's definitely python 2 again
Sviatoslav Sydorenko
@webknjaz
Nov 14 2018 18:46
no
under py2
Giancarlos
@Giancarlos
Nov 14 2018 18:46
oh? pebkac?
Sviatoslav Sydorenko
@webknjaz
Nov 14 2018 18:47
are you sure you wrote object lowercase?
Giancarlos
@Giancarlos
Nov 14 2018 18:47
doh!
Sviatoslav Sydorenko
@webknjaz
Nov 14 2018 18:47
because in your snippet it's wrong
and with metaclass line you don't need to explicitly inherit things from object anymore
Giancarlos
@Giancarlos
Nov 14 2018 18:47
I was doing ctrl + z a second ago and forgot to go back enough
oh it is pekac, I edited the wrong file
ah! it works woo
the only thing that breaks is the SEO URL trick, but I can live with this for now e.g. /ui/people/1/2 vs /ui/people/?boop=1&beep=2
Sviatoslav Sydorenko
@webknjaz
Nov 14 2018 18:50
well
you could try adjusting that as well
default method works like this
Giancarlos
@Giancarlos
Nov 14 2018 18:51
oh **kwargs fixed it (was testing without it on the correct file)
def index(self, **kwargs):
Jason R. Coombs
@jaraco
Nov 14 2018 18:52
\o/
Giancarlos
@Giancarlos
Nov 14 2018 18:53
you guys rock, this was eating at me for a bit
Sviatoslav Sydorenko
@webknjaz
Nov 14 2018 18:53
\o/
Giancarlos
@Giancarlos
Nov 14 2018 19:46
crud I think I have a separate issue, but it might be me using JS wrong
Giancarlos
@Giancarlos
Nov 14 2018 19:56
nvm ran a test it works
metaprogramming is magical