Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]
Groups > comp.lang.python > #60364 > unrolled thread
| Started by | Devin Jeanpierre <jeanpierreda@gmail.com> |
|---|---|
| First post | 2013-11-24 05:04 -0800 |
| Last post | 2013-11-25 01:57 +1100 |
| Articles | 13 — 5 participants |
Back to article view | Back to comp.lang.python
This discussion starts older than the indexed window; earlier articles aren't shown. The article labeled Started by
below is the oldest one visible, not the original post.
Re: Implement logic on object.attribute and object.attribute() Devin Jeanpierre <jeanpierreda@gmail.com> - 2013-11-24 05:04 -0800
Re: Implement logic on object.attribute and object.attribute() Steven D'Aprano <steve+comp.lang.python@pearwood.info> - 2013-11-24 13:45 +0000
Re: Implement logic on object.attribute and object.attribute() Devin Jeanpierre <jeanpierreda@gmail.com> - 2013-11-24 06:04 -0800
Re: Implement logic on object.attribute and object.attribute() Chris Angelico <rosuav@gmail.com> - 2013-11-25 01:13 +1100
Re: Implement logic on object.attribute and object.attribute() Roy Smith <roy@panix.com> - 2013-11-24 09:21 -0500
Re: Implement logic on object.attribute and object.attribute() Chris Angelico <rosuav@gmail.com> - 2013-11-25 01:31 +1100
Re: Implement logic on object.attribute and object.attribute() Chris Angelico <rosuav@gmail.com> - 2013-11-25 01:13 +1100
Re: Implement logic on object.attribute and object.attribute() Marc Aymerich <glicerinu@gmail.com> - 2013-11-24 15:16 +0100
Re: Implement logic on object.attribute and object.attribute() Chris Angelico <rosuav@gmail.com> - 2013-11-25 01:37 +1100
Re: Implement logic on object.attribute and object.attribute() Steven D'Aprano <steve+comp.lang.python@pearwood.info> - 2013-11-24 15:11 +0000
Re: Implement logic on object.attribute and object.attribute() Marc Aymerich <glicerinu@gmail.com> - 2013-11-24 15:34 +0100
Re: Implement logic on object.attribute and object.attribute() Marc Aymerich <glicerinu@gmail.com> - 2013-11-24 15:48 +0100
Re: Implement logic on object.attribute and object.attribute() Chris Angelico <rosuav@gmail.com> - 2013-11-25 01:57 +1100
| From | Devin Jeanpierre <jeanpierreda@gmail.com> |
|---|---|
| Date | 2013-11-24 05:04 -0800 |
| Subject | Re: Implement logic on object.attribute and object.attribute() |
| Message-ID | <mailman.3128.1385298299.18130.python-list@python.org> |
On Sun, Nov 24, 2013 at 4:52 AM, Marc Aymerich <glicerinu@gmail.com> wrote: > Hi, > I'm playing with python internals to make objects behave like this: > > if I access to "object.attribute" I want to return the result of an > HTTP GET request. However if i call "object.attribute()" I want an > HTTP POST request to be executed. Uh oh. What you want is impossible. You cannot call an attribute without first accessing it. :( -- Devin
[toc] | [next] | [standalone]
| From | Steven D'Aprano <steve+comp.lang.python@pearwood.info> |
|---|---|
| Date | 2013-11-24 13:45 +0000 |
| Message-ID | <529202e3$0$29993$c3e8da3$5496439d@news.astraweb.com> |
| In reply to | #60364 |
On Sun, 24 Nov 2013 05:04:16 -0800, Devin Jeanpierre wrote:
> On Sun, Nov 24, 2013 at 4:52 AM, Marc Aymerich <glicerinu@gmail.com>
> wrote:
>> Hi,
>> I'm playing with python internals to make objects behave like this:
>>
>> if I access to "object.attribute" I want to return the result of an
>> HTTP GET request. However if i call "object.attribute()" I want an HTTP
>> POST request to be executed.
>
> Uh oh. What you want is impossible. You cannot call an attribute without
> first accessing it. :(
Not quite impossible. All you need is an object that behaves like a
string, except it has a __call__ method. Here's a sketch of a solution,
completely untested.
class CallableString(str):
# Like a string, but callable.
def function(self):
raise NotImplementedError(
"this must be overridden on the instance"
)
def __call__(self):
return self.function()
class Magic_HTTP_Thing:
@property
def attribute(self):
result = CallableStr(self.do_get())
result.function = lambda: self.do_put()
def do_get(self):
# Do a HTTP GET request.
return "Get stuff"
def do_put(self):
# Do a HTTP PUT request.
return "Put stuff"
Possible or not, it doesn't seem like a reasonable API to me.
--
Steven
[toc] | [prev] | [next] | [standalone]
| From | Devin Jeanpierre <jeanpierreda@gmail.com> |
|---|---|
| Date | 2013-11-24 06:04 -0800 |
| Message-ID | <mailman.3130.1385301889.18130.python-list@python.org> |
| In reply to | #60366 |
On Sun, Nov 24, 2013 at 5:45 AM, Steven D'Aprano <steve+comp.lang.python@pearwood.info> wrote: > On Sun, 24 Nov 2013 05:04:16 -0800, Devin Jeanpierre wrote: >> Uh oh. What you want is impossible. You cannot call an attribute without >> first accessing it. :( > > Not quite impossible. All you need is an object that behaves like a > string, except it has a __call__ method. Here's a sketch of a solution, > completely untested. I admit that thought crossed my mind, but I assumed he didn't want a GET+POST, and also "impossible" is often a nice shorthand for "the possibility is extraordinarily awful". -- Devin
[toc] | [prev] | [next] | [standalone]
| From | Chris Angelico <rosuav@gmail.com> |
|---|---|
| Date | 2013-11-25 01:13 +1100 |
| Message-ID | <mailman.3131.1385302390.18130.python-list@python.org> |
| In reply to | #60366 |
On Mon, Nov 25, 2013 at 12:45 AM, Steven D'Aprano <steve+comp.lang.python@pearwood.info> wrote: > Not quite impossible. All you need is an object that behaves like a > string, except it has a __call__ method. Here's a sketch of a solution, > completely untested. > > class Magic_HTTP_Thing: > @property > def attribute(self): > result = CallableStr(self.do_get()) > result.function = lambda: self.do_put() Problem with that is that it'll still call do_get immediately. You'd have to somehow defer this call until it's actually _used_, which is why I dropped a mention of "converting to str?" - which would presumably be a __str__ method. But I still don't like the API. ChrisA
[toc] | [prev] | [next] | [standalone]
| From | Roy Smith <roy@panix.com> |
|---|---|
| Date | 2013-11-24 09:21 -0500 |
| Message-ID | <roy-4F23EB.09214724112013@news.panix.com> |
| In reply to | #60368 |
In article <mailman.3131.1385302390.18130.python-list@python.org>, Chris Angelico <rosuav@gmail.com> wrote: > On Mon, Nov 25, 2013 at 12:45 AM, Steven D'Aprano > <steve+comp.lang.python@pearwood.info> wrote: > > Not quite impossible. All you need is an object that behaves like a > > string, except it has a __call__ method. Here's a sketch of a solution, > > completely untested. > > > > class Magic_HTTP_Thing: > > @property > > def attribute(self): > > result = CallableStr(self.do_get()) > > result.function = lambda: self.do_put() > > Problem with that is that it'll still call do_get immediately. You'd > have to somehow defer this call until it's actually _used_, which is > why I dropped a mention of "converting to str?" - which would > presumably be a __str__ method. But I still don't like the API. > > ChrisA If the REST interface is designed properly, all the GETs are nullipotent, so modulo efficiency, it should all work. But, yeah, this seems really ugly.
[toc] | [prev] | [next] | [standalone]
| From | Chris Angelico <rosuav@gmail.com> |
|---|---|
| Date | 2013-11-25 01:31 +1100 |
| Message-ID | <mailman.3135.1385303515.18130.python-list@python.org> |
| In reply to | #60373 |
On Mon, Nov 25, 2013 at 1:21 AM, Roy Smith <roy@panix.com> wrote: > If the REST interface is designed properly, all the GETs are > nullipotent, so modulo efficiency, it should all work. Yes, but "modulo efficiency" is not something you want to do when you're talking network traffic. If this were just allocating a bit of memory, then I might be inclined to let it duplicate the work (if the API were sufficiently beautiful to justify it, which IMHO this one isn't), but a full HTTP round trip? And this is presumably all synchronous, as otherwise all sorts of things would get messy (you'd have a string that doesn't have a value yet... could be awkward). ChrisA
[toc] | [prev] | [next] | [standalone]
| From | Chris Angelico <rosuav@gmail.com> |
|---|---|
| Date | 2013-11-25 01:13 +1100 |
| Message-ID | <mailman.3132.1385302428.18130.python-list@python.org> |
| In reply to | #60366 |
On Mon, Nov 25, 2013 at 1:04 AM, Devin Jeanpierre <jeanpierreda@gmail.com> wrote: > and also "impossible" is often a nice shorthand for "the > possibility is extraordinarily awful". +1 QOTW! ChrisA
[toc] | [prev] | [next] | [standalone]
| From | Marc Aymerich <glicerinu@gmail.com> |
|---|---|
| Date | 2013-11-24 15:16 +0100 |
| Message-ID | <mailman.3133.1385302626.18130.python-list@python.org> |
| In reply to | #60366 |
On Sun, Nov 24, 2013 at 2:45 PM, Steven D'Aprano <steve+comp.lang.python@pearwood.info> wrote: > On Sun, 24 Nov 2013 05:04:16 -0800, Devin Jeanpierre wrote: > >> On Sun, Nov 24, 2013 at 4:52 AM, Marc Aymerich <glicerinu@gmail.com> >> wrote: >>> Hi, >>> I'm playing with python internals to make objects behave like this: >>> >>> if I access to "object.attribute" I want to return the result of an >>> HTTP GET request. However if i call "object.attribute()" I want an HTTP >>> POST request to be executed. >> >> Uh oh. What you want is impossible. You cannot call an attribute without >> first accessing it. :( > > Not quite impossible. All you need is an object that behaves like a > string, except it has a __call__ method. Here's a sketch of a solution, > completely untested. > > > class CallableString(str): > # Like a string, but callable. > def function(self): > raise NotImplementedError( > "this must be overridden on the instance" > ) > def __call__(self): > return self.function() > > > class Magic_HTTP_Thing: > @property > def attribute(self): > result = CallableStr(self.do_get()) > result.function = lambda: self.do_put() > def do_get(self): > # Do a HTTP GET request. > return "Get stuff" > def do_put(self): > # Do a HTTP PUT request. > return "Put stuff" OMG steven, it actually works :) >>> class CallableString(str): ... # Like a string, but callable. ... def function(self): ... raise NotImplementedError( ... "this must be overridden on the instance" ... ) ... def __call__(self): ... return self.function() ... >>> >>> class Magic_HTTP_Thing: ... @property ... def attribute(self): ... result = CallableString(self.do_get()) ... result.function = lambda: self.do_put() ... return result ... def do_get(self): ... # Do a HTTP GET request. ... return "Get stuff" ... def do_put(self): ... # Do a HTTP PUT request. ... return "Put stuff" ... >>> >>> Magic_HTTP_Thing().attribute 'Get stuff' >>> Magic_HTTP_Thing().attribute() 'Put stuff' > > Possible or not, it doesn't seem like a reasonable API to me. yeah, this is a "corner case" of our REST API, I have some badly design endpoints that mostly behave like functions, but some of them also contain state information that you can GET, I was trying to map this behavior to python objects and this interface is the best that occurred to me :) -- Marc
[toc] | [prev] | [next] | [standalone]
| From | Chris Angelico <rosuav@gmail.com> |
|---|---|
| Date | 2013-11-25 01:37 +1100 |
| Message-ID | <mailman.3136.1385303840.18130.python-list@python.org> |
| In reply to | #60366 |
On Mon, Nov 25, 2013 at 1:16 AM, Marc Aymerich <glicerinu@gmail.com> wrote:
> ... def do_get(self):
> ... # Do a HTTP GET request.
> ... return "Get stuff"
> ... def do_put(self):
> ... # Do a HTTP PUT request.
> ... return "Put stuff"
To make this a bit more realistic, try this instead - tying in with
what I said in response to Roy:
class CallableString(str):
# Like a string, but callable.
def function(self):
raise NotImplementedError(
"this must be overridden on the instance"
)
def __call__(self):
return self.function()
class Magic_HTTP_Thing:
@property
def attribute(self):
result = CallableString(self.do_get())
result.function = lambda: self.do_put()
return result
def do_get(self):
# Do a HTTP GET request.
print("Doing the GET call, please wait...")
time.sleep(1)
return "Get stuff"
def do_put(self):
# Do a HTTP PUT request.
print("Doing the PUT call, please wait...")
time.sleep(1)
return "Put stuff"
(PUT or POST, makes no difference; I think you were looking for POST,
but Steven wrote PUT here so I'll use that for simplicity)
>>> Magic_HTTP_Thing().attribute()
Doing the GET call, please wait...
Doing the PUT call, please wait...
'Put stuff'
And that's what you don't want happening. Your PUT / POST calls are
going to take twice as long as they should.
ChrisA
[toc] | [prev] | [next] | [standalone]
| From | Steven D'Aprano <steve+comp.lang.python@pearwood.info> |
|---|---|
| Date | 2013-11-24 15:11 +0000 |
| Message-ID | <5292172c$0$29993$c3e8da3$5496439d@news.astraweb.com> |
| In reply to | #60378 |
On Mon, 25 Nov 2013 01:37:12 +1100, Chris Angelico wrote:
> class Magic_HTTP_Thing:
> @property
> def attribute(self):
> result = CallableString(self.do_get())
> result.function = lambda: self.do_put()
> return result
> def do_get(self):
> # Do a HTTP GET request.
> print("Doing the GET call, please wait...")
> time.sleep(1)
> return "Get stuff"
> def do_put(self):
> # Do a HTTP PUT request.
> print("Doing the PUT call, please wait...")
> time.sleep(1)
> return "Put stuff"
>
> (PUT or POST, makes no difference; I think you were looking for POST,
> but Steven wrote PUT here so I'll use that for simplicity)
Oops, did I screw that bit up? Sorry.
One possible solution here is to cache the GET so it only occurs once.
def do_get(self):
# Do a HTTP GET request.
if hasattr(self, "_result"):
result = self._result
else:
print("Doing the GET call, please wait...")
time.sleep(1)
result = self._result = "Get stuff"
return result
You can give the cache a timestamp as well, in case you need to
invalidate the cache once it reaches a certain age. I'll leave that as an
exercise.
--
Steven
[toc] | [prev] | [next] | [standalone]
| From | Marc Aymerich <glicerinu@gmail.com> |
|---|---|
| Date | 2013-11-24 15:34 +0100 |
| Message-ID | <mailman.3138.1385304139.18130.python-list@python.org> |
| In reply to | #60366 |
On Sun, Nov 24, 2013 at 3:13 PM, Chris Angelico <rosuav@gmail.com> wrote: > On Mon, Nov 25, 2013 at 12:45 AM, Steven D'Aprano > <steve+comp.lang.python@pearwood.info> wrote: >> Not quite impossible. All you need is an object that behaves like a >> string, except it has a __call__ method. Here's a sketch of a solution, >> completely untested. >> >> class Magic_HTTP_Thing: >> @property >> def attribute(self): >> result = CallableStr(self.do_get()) >> result.function = lambda: self.do_put() > > Problem with that is that it'll still call do_get immediately. You'd > have to somehow defer this call until it's actually _used_, which is > why I dropped a mention of "converting to str?" - which would > presumably be a __str__ method. But I still don't like the API. That's right. In my case deferring the GET call will not be a problem since this objects will be used in just a few particular places and the workflow is always something like: # Initiate firmware building node.ctl.firmware() # wait until finished while node.ctl.firmware.progress < 100: time.sleep(1) Thanks for sharing your knowledge guys !! -- Marc
[toc] | [prev] | [next] | [standalone]
| From | Marc Aymerich <glicerinu@gmail.com> |
|---|---|
| Date | 2013-11-24 15:48 +0100 |
| Message-ID | <mailman.3139.1385304915.18130.python-list@python.org> |
| In reply to | #60366 |
On Sun, Nov 24, 2013 at 3:37 PM, Chris Angelico <rosuav@gmail.com> wrote:
> On Mon, Nov 25, 2013 at 1:16 AM, Marc Aymerich <glicerinu@gmail.com> wrote:
>> ... def do_get(self):
>> ... # Do a HTTP GET request.
>> ... return "Get stuff"
>> ... def do_put(self):
>> ... # Do a HTTP PUT request.
>> ... return "Put stuff"
>
> To make this a bit more realistic, try this instead - tying in with
> what I said in response to Roy:
>
> class CallableString(str):
> # Like a string, but callable.
> def function(self):
> raise NotImplementedError(
> "this must be overridden on the instance"
> )
> def __call__(self):
> return self.function()
>
>
> class Magic_HTTP_Thing:
> @property
> def attribute(self):
> result = CallableString(self.do_get())
> result.function = lambda: self.do_put()
> return result
> def do_get(self):
> # Do a HTTP GET request.
> print("Doing the GET call, please wait...")
> time.sleep(1)
> return "Get stuff"
> def do_put(self):
> # Do a HTTP PUT request.
> print("Doing the PUT call, please wait...")
> time.sleep(1)
> return "Put stuff"
>
> (PUT or POST, makes no difference; I think you were looking for POST,
> but Steven wrote PUT here so I'll use that for simplicity)
>
>>>> Magic_HTTP_Thing().attribute()
> Doing the GET call, please wait...
> Doing the PUT call, please wait...
> 'Put stuff'
>
> And that's what you don't want happening. Your PUT / POST calls are
> going to take twice as long as they should.
Thanks Chris,
didn't realize about the implicit GET when calling the attribute :(
I think I'll put the get call on __repr__, __str__ and __getattr__,
something like
class HTTPAttribute(object):
""" functional endpoint representation """
def __repr__(self):
self._retrieve()
return json.dumps(self.__dict__)
def __str__(self):
self._retrieve()
return json.dumps(self.__dict__)
def __init__(self, resource, uri):
self._resource = resource
self.uri = uri
def __call__(self, *args, **kwargs):
return self._resource._api.post(self.uri, *args, **kwargs).content
def __getattr__(self, name):
self._retrieve()
if hasattr(self, name):
return getattr(self, name)
raise AttributeError("'%s' object has no attribute '%s'" %
(str(type(self)), name))
def _retrieve(self):
resource = self._resource._api.retrieve(self.uri)
for key, value in resource._data.iteritems():
setattr(self, key, value)
and that's it,
But still I'll reconsider an interface with less magic :P
--
Marc
[toc] | [prev] | [next] | [standalone]
| From | Chris Angelico <rosuav@gmail.com> |
|---|---|
| Date | 2013-11-25 01:57 +1100 |
| Message-ID | <mailman.3140.1385305076.18130.python-list@python.org> |
| In reply to | #60366 |
On Mon, Nov 25, 2013 at 1:48 AM, Marc Aymerich <glicerinu@gmail.com> wrote: > But still I'll reconsider an interface with less magic :P Yeah, I would definitely recommend that :) Magic can be fun sometimes, but it's often not worth the hassle. ChrisA
[toc] | [prev] | [standalone]
Back to top | Article view | comp.lang.python
csiph-web