google-code-export / psutil

Automatically exported from code.google.com/p/psutil
Other
0 stars 0 forks source link

psutil._common.constants will not serialize properly to json #408

Closed GoogleCodeExporter closed 9 years ago

GoogleCodeExporter commented 9 years ago
What steps will reproduce the problem?
a = psutil._common.CONN_LISTEN
b = [a, a]
c = json.dumps(b)
print c

What is the expected output?
'["LISTEN", "LISTEN"]'

What do you see instead?
'[LISTEN, LISTEN]'

What version of psutil are you using? What Python version?
psutil 1.0.1
ActivePython 2.7.2.5 (Windows 7SP1, x86)

---
psutil 1.0.1
python 2.7.2 (Mac OS X 10.8.4)

On what operating system? Is it 32bit or 64bit version?
32-bit

Please provide any additional information below.
There is a small distinction between the outputs.  Because json detects the 
constant as type int, it outputs the string without the surrounded double 
quotes.

The error occurs when you attempt to call json.loads(c) on the generated 
output, it will raise an exception that it is an invalid json string.

Original issue reported on code.google.com by thep...@gmail.com on 17 Jul 2013 at 11:47

GoogleCodeExporter commented 9 years ago
Mmmm interesting. I wouldn't know how to fix this off hand. I will try to think 
about this later.
For the record: you should use psutil.CONN_LISTEN, not 
psutil._common.CONN_LISTEN.

Original comment by g.rodola on 18 Jul 2013 at 2:09

GoogleCodeExporter commented 9 years ago
I'm not directly referencing psutil.CONN_LISTEN, it's being returned as part of 
the process attributes.

For example:
process_list = [proc.as_dict() for proc in psutil.process_iter()]
return json.dumps(process_list)

I have a work around which involves stack inspection for the __str__ call but 
it's not pretty.  It's good enough for me to use right now.  It's not 100% 
correct either since strings are supposed to be passed through 
json._iterencode._encode.

    def __str__(self):
        frame, filename, lineno, funcname, lines, idx = inspect.stack()[1]
        if 'json' in filename and funcname == 'iterencode':
            return '"%s"' % self._name
        return self._name

The real fix would be some how make isinstance(psutil._common.constants, 
basestring) return true as that's how the json encoder is iterating through 
elements:

            if isinstance(value, basestring):
                yield buf + _encoder(value)
            elif value is None:
                yield buf + 'null'
            elif value is True:
                yield buf + 'true'
            elif value is False:
                yield buf + 'false'
            elif isinstance(value, (int, long)):
                yield buf + str(value)
            elif isinstance(value, float):
                yield buf + _floatstr(value)

Original comment by thep...@gmail.com on 18 Jul 2013 at 2:33

GoogleCodeExporter commented 9 years ago
It seems we migth use __instancecheck__ special method in order to make 
json.loads() believe it's dealing with a string. 

I'm looking into how to make it work now as apparently you have to use a 
metaclass (if I just define it never gets invoked).

It's not an easy problem to fix though, also because __instancecheck__ works on 
python >= 2.6 only.

Original comment by g.rodola on 18 Jul 2013 at 4:15

GoogleCodeExporter commented 9 years ago
The other side of this is how to unserialize?  if loads() gets "LISTEN" and 
returns the string "LISTEN" instead of the constant psutil.CONN_LISTEN, is that 
going to be correct?

Original comment by ethan.st...@gmail.com on 18 Jul 2013 at 6:37

GoogleCodeExporter commented 9 years ago
It seems__instancecheck__ won't help either as it would have to be applied to 
str/basestring, not the constant class.

Also, it looks like they're facing the same problem with Python 3.4's Enum, see:
http://bugs.python.org/issue18264.

As of now I'm not seeing any completely satisying solution to solve this 
problem except maybe turning constants into strings and be done with it. That 
way we would also preserve backward compatibility.

Other solutions such as making as_dict() cast constants to int or str, using 
inspect from within __str__ or writing a custom json handler all look 
sub-optimal to me for one reason or another.

Side note for the sake of completeness: the same problem applies for 
psutil.STATUS_* constants returned by Process.status.

Original comment by g.rodola on 18 Jul 2013 at 11:42

GoogleCodeExporter commented 9 years ago
I found myself thinking about this issue. My first idea would be to use an 
integer for representing the connection state (e. g. short and easy). However, 
after I checked the TCP protocol specification:

http://tools.ietf.org/html/rfc793

It doesn't really uses "int" but a very well know collection of labels. Could 
be possible that instead of using constants or integer/string representations 
of constantes, to use strings of those labels for representing connection 
states?

Regards,

Original comment by renatosa...@gmail.com on 27 Jul 2013 at 1:20

GoogleCodeExporter commented 9 years ago
Yes, using plain python strings looks like the best way to go.

Original comment by g.rodola on 29 Jul 2013 at 2:19

GoogleCodeExporter commented 9 years ago
Change committed in revision ab4a1f9dc57c.

Original comment by g.rodola on 9 Aug 2013 at 7:59

GoogleCodeExporter commented 9 years ago

Original comment by g.rodola on 28 Sep 2013 at 10:06