2

The dis.dis() function in the dis module allows one to disassemble raw bytecode from a code object into a human-readable form. The documentation for dis() states what may be passed into the function:

Disassemble the bytesource object. bytesource can denote either a module, a class, a method, a function, or a code object. For a module, it disassembles all functions. For a class, it disassembles all methods. For a single code sequence, it prints one line per bytecode instruction. If no object is provided, it disassembles the last traceback.

When experimenting with the module, everything works as expected. However I was surprised when I passed a str object into function and it ran fine:

>>> import dis
>>> dis.dis('1 + 2')
          0 <49>           
          1 SLICE+2        
          2 STORE_SLICE+3  
          3 SLICE+2        
          4 DELETE_SLICE+0 
>>> 

The documentation for dis() specifically said what was allowed to be passed to the function. And the last time I checked, a str does not satisfy any of those requirements. Per the documentation for code objects:

Code objects represent byte-compiled executable Python code, or bytecode. [...]

But what surprised me even more, was the bytecode that dis() has generated. What does this bytecode mean? I decied to check and see what each opcode meant by looking at 32.12.1. Python Bytecode Instructions on the documentation page for dis. This however confused me even more. Some opcodes were not even documented(<49>).

What exactly is Python trying to do with the string I passed in. Is it considering my string to be literal source code? Or is attempting to construct a string from what I've passed in?

I would assume my former guess to be correct, due to looking over the source code for dis.disassemble_string()(Which is whatdis.dis() calls when a str object is passed in). But if that is true, then why does the bytecode look so strange? If I pass in a function with the same expression, the bytecode makes perfect sense:

>>> def func():
...     1 + 2
... 
>>> dis.dis(func)
  2           0 LOAD_CONST               3 (3)
              3 POP_TOP             
              4 LOAD_CONST               0 (None)
              7 RETURN_VALUE        
>>> 

This behavior doesn't seem to be limited to expression, though. I tried several other statements, and they all generated the same weird-looking bytecode:

>>> dis.dis('foo = 10')
          0 BUILD_TUPLE     28527
          3 SLICE+2        
          4 DELETE_SUBSCR  
          5 SLICE+2        
          6 <49>           
          7 <48>           
>>> dis.dis('print 0')
          0 JUMP_IF_TRUE_OR_POP 26994
          3 JUMP_FORWARD     8308 (to 8314)
          6 <48>           
>>> # etc...

Is this behavior documented somewhere? I looked over the entire documentation page for the dis module, but I didn't find any relevant information.

Christian Dean
  • 22,138
  • 7
  • 54
  • 87
  • in python 2 there is no distinction between "character string" and "bytestring" so it is trying to interpret your words as python bytecode – Tadhg McDonald-Jensen Dec 18 '16 at 05:56
  • the `<49>` is coming from `ord('1')`, it doesn't mean anything as a bytecode command so dis displays it like that to indicate "what ever this byte is" – Tadhg McDonald-Jensen Dec 18 '16 at 05:57
  • @TadhgMcDonald-Jensen The question you linked to _could_ be a duplicate, but I believe the question asker is asking about a very specfic case. And the selected answer given is more about trying to help solve the OP's specfic case, rather than explaining why he is having a problem. My question is more of a general case. – Christian Dean Dec 18 '16 at 05:59
  • 1
    I'm sorry I marked the wrong one, I think I meant to use [this one](http://stackoverflow.com/questions/12673074/how-should-i-understand-the-output-of-dis-dis) as the duplicate target, my bad. – Tadhg McDonald-Jensen Dec 18 '16 at 06:05
  • @TadhgMcDonald-Jensen Now that duplicate helped ;) I'll mark my question as a dupe now. – Christian Dean Dec 18 '16 at 06:09
  • That is indeed a closely related question, but I don't think it's quite a duplicate. I think it'd be good to post an answer to this question that references the other one. – David Z Dec 18 '16 at 06:10
  • @DavidZ _Whooooopsie!_ I guess I was a little to hasty..... – Christian Dean Dec 18 '16 at 06:11
  • 2
    Eh, no big deal. At least people coming across this later will find the information they need! – David Z Dec 18 '16 at 06:11
  • The biggest thing that my [first comment](http://stackoverflow.com/questions/41205848/what-is-the-dis-function-doing-when-i-pass-in-a-string?noredirect=1#comment69610945_41205848) may not explain well enough is that bytecode in python2 is a string, I.E. `type(compile("1+2","","eval").co_code) == ` so it does satisfy one of the argument accepted. (in python 3 bytecode is type `bytes` so there isn't the same ambiguity) – Tadhg McDonald-Jensen Dec 18 '16 at 06:25
  • @TadhgMcDonald-Jensen Got you. Thanks for explanations. – Christian Dean Dec 18 '16 at 06:26

0 Answers0