1 """Python bytecode generator
3 Currently contains generic ASTVisitor code, a LocalNameFinder, and a
4 CodeGenerator. Eventually, this will get split into the ASTVisitor as
5 a generic tool and CodeGenerator as a specific tool.
8 from p2c
import transformer
, ast
9 from pyassem
import StackRef
, PyAssembler
25 t
= transformer
.Transformer()
26 return t
.parsesuite(src
)
28 def walk(tree
, visitor
, verbose
=None, walker
=None):
33 if verbose
is not None:
35 w
.preorder(tree
, visitor
)
40 for attr
in dir(node
):
42 print "\t", "%-10.10s" % attr
, getattr(node
, attr
)
45 """Performs a depth-first walk of the AST
47 The ASTVisitor will walk the AST, performing either a preorder or
48 postorder traversal depending on which method is called.
51 preorder(tree, visitor)
52 postorder(tree, visitor)
53 tree: an instance of ast.Node
54 visitor: an instance with visitXXX methods
56 The ASTVisitor is responsible for walking over the tree in the
57 correct order. For each node, it checks the visitor argument for
58 a method named 'visitNodeType' where NodeType is the name of the
59 node's class, e.g. Classdef. If the method exists, it is called
60 with the node as its sole argument.
62 The visitor method for a particular node type can control how
63 child nodes are visited during a preorder walk. (It can't control
64 the order during a postorder walk, because it is called _after_
65 the walk has occurred.) The ASTVisitor modifies the visitor
66 argument by adding a visit method to the visitor; this method can
67 be used to visit a particular child node. If the visitor method
68 returns a true value, the ASTVisitor will not traverse the child
71 XXX The interface for controlling the preorder walk needs to be
72 re-considered. The current interface is convenient for visitors
73 that mostly let the ASTVisitor do everything. For something like
74 a code generator, where you want to walk to occur in a specific
75 order, it's a pain to add "return 1" to the end of each method.
77 XXX Perhaps I can use a postorder walk for the code generator?
85 def preorder(self
, tree
, visitor
):
86 """Do preorder walk of tree using visitor"""
87 self
.visitor
= visitor
88 visitor
.visit
= self
._preorder
91 def _preorder(self
, node
):
92 stop
= self
.dispatch(node
)
95 for child
in node
.getChildren():
96 if isinstance(child
, ast
.Node
):
99 def postorder(self
, tree
, visitor
):
100 """Do preorder walk of tree using visitor"""
101 self
.visitor
= visitor
102 visitor
.visit
= self
._postorder
103 self
._postorder
(tree
)
105 def _postorder(self
, tree
):
106 for child
in node
.getChildren():
107 if isinstance(child
, ast
.Node
):
108 self
._preorder
(child
)
111 def dispatch(self
, node
):
113 className
= node
.__class
__.__name
__
114 meth
= getattr(self
.visitor
, 'visit' + className
, None)
116 if self
.VERBOSE
== 1:
118 print "dispatch", className
120 print "dispatch", className
, (meth
and meth
.__name
__ or '')
124 class ExampleASTVisitor(ASTVisitor
):
125 """Prints examples of the nodes that aren't visited
127 This visitor-driver is only useful for development, when it's
128 helpful to develop a visitor incremently, and get feedback on what
129 you still have to do.
133 def dispatch(self
, node
):
135 className
= node
.__class
__.__name
__
136 meth
= getattr(self
.visitor
, 'visit' + className
, None)
138 if self
.VERBOSE
== 1:
140 print "dispatch", className
142 print "dispatch", className
, (meth
and meth
.__name
__ or '')
146 klass
= node
.__class
__
148 if self
.examples
.has_key(klass
):
150 self
.examples
[klass
] = klass
153 for attr
in dir(node
):
155 print "\t", "%-12.12s" % attr
, getattr(node
, attr
)
159 """Generate bytecode for the Python VM"""
163 # XXX should clean up initialization and generateXXX funcs
164 def __init__(self
, filename
="<?>"):
165 self
.filename
= filename
166 self
.code
= PyAssembler()
167 self
.code
.setFlags(0)
168 self
.locals = misc
.Stack()
169 self
.loops
= misc
.Stack()
174 def emit(self
, *args
):
175 # XXX could just use self.emit = self.code.emit
176 apply(self
.code
.emit
, args
)
178 def _generateFunctionOrLambdaCode(self
, func
):
179 self
.name
= func
.name
180 self
.filename
= filename
182 # keep a lookout for 'def foo((x,y)):'
183 args
, hasTupleArg
= self
.generateArglist(func
.argnames
)
185 self
.code
= PyAssembler(args
=args
, name
=func
.name
,
187 self
.namespace
= self
.OPTIMIZED
189 self
.code
.setVarArgs()
191 self
.code
.setKWArgs()
192 lnf
= walk(func
.code
, LocalNameFinder(args
), 0)
193 self
.locals.push(lnf
.getLocals())
194 self
.emit('SET_LINENO', func
.lineno
)
196 self
.generateArgUnpack(func
.argnames
)
197 walk(func
.code
, self
)
199 def generateArglist(self
, arglist
):
204 if type(elt
) == types
.StringType
:
206 elif type(elt
) == types
.TupleType
:
207 args
.append(".nested%d" % count
)
209 extra
.extend(misc
.flatten(elt
))
211 raise ValueError, "unexpect argument type:", elt
212 return args
+ extra
, count
214 def generateArgUnpack(self
, args
):
217 if type(arg
) == types
.TupleType
:
218 self
.emit('LOAD_FAST', '.nested%d' % count
)
220 self
.unpackTuple(arg
)
222 def unpackTuple(self
, tup
):
223 self
.emit('UNPACK_TUPLE', len(tup
))
225 if type(elt
) == types
.TupleType
:
226 self
.unpackTuple(elt
)
228 self
.emit('STORE_FAST', elt
)
230 def generateFunctionCode(self
, func
):
231 """Generate code for a function body"""
232 self
._generateFunctionOrLambdaCode
(func
)
233 self
.emit('LOAD_CONST', None)
234 self
.emit('RETURN_VALUE')
236 def generateLambdaCode(self
, func
):
237 self
._generateFunctionOrLambdaCode
(func
)
238 self
.emit('RETURN_VALUE')
240 def generateClassCode(self
, klass
):
241 self
.code
= PyAssembler(name
=klass
.name
,
243 self
.emit('SET_LINENO', klass
.lineno
)
244 lnf
= walk(klass
.code
, LocalNameFinder(), 0)
245 self
.locals.push(lnf
.getLocals())
246 walk(klass
.code
, self
)
247 self
.emit('LOAD_LOCALS')
248 self
.emit('RETURN_VALUE')
251 """Create a Python code object."""
252 if self
.namespace
== self
.OPTIMIZED
:
253 self
.code
.setOptimized()
254 return self
.code
.makeCodeObject()
256 def isLocalName(self
, name
):
257 return self
.locals.top().has_elt(name
)
259 def _nameOp(self
, prefix
, name
):
260 if self
.isLocalName(name
):
261 if self
.namespace
== self
.OPTIMIZED
:
262 self
.emit(prefix
+ '_FAST', name
)
264 self
.emit(prefix
+ '_NAME', name
)
266 self
.emit(prefix
+ '_GLOBAL', name
)
268 def storeName(self
, name
):
269 self
._nameOp
('STORE', name
)
271 def loadName(self
, name
):
272 self
._nameOp
('LOAD', name
)
274 def delName(self
, name
):
275 self
._nameOp
('DELETE', name
)
277 def visitNULL(self
, node
):
278 """Method exists only to stop warning in -v mode"""
281 visitStmt
= visitNULL
282 visitGlobal
= visitNULL
284 def visitDiscard(self
, node
):
285 self
.visit(node
.expr
)
289 def visitPass(self
, node
):
290 self
.emit('SET_LINENO', node
.lineno
)
292 def visitModule(self
, node
):
293 lnf
= walk(node
.node
, LocalNameFinder(), 0)
294 self
.locals.push(lnf
.getLocals())
295 self
.visit(node
.node
)
296 self
.emit('LOAD_CONST', None)
297 self
.emit('RETURN_VALUE')
300 def visitImport(self
, node
):
301 self
.emit('SET_LINENO', node
.lineno
)
302 for name
in node
.names
:
303 self
.emit('IMPORT_NAME', name
)
306 def visitFrom(self
, node
):
307 self
.emit('SET_LINENO', node
.lineno
)
308 self
.emit('IMPORT_NAME', node
.modname
)
309 for name
in node
.names
:
312 self
.emit('IMPORT_FROM', name
)
315 def visitClassdef(self
, node
):
316 self
.emit('SET_LINENO', node
.lineno
)
317 self
.emit('LOAD_CONST', node
.name
)
318 for base
in node
.bases
:
320 self
.emit('BUILD_TUPLE', len(node
.bases
))
321 classBody
= CodeGenerator(self
.filename
)
322 classBody
.generateClassCode(node
)
323 self
.emit('LOAD_CONST', classBody
)
324 self
.emit('MAKE_FUNCTION', 0)
325 self
.emit('CALL_FUNCTION', 0)
326 self
.emit('BUILD_CLASS')
327 self
.storeName(node
.name
)
330 def _visitFuncOrLambda(self
, node
, kind
):
331 """Code common to Function and Lambda nodes"""
332 codeBody
= CodeGenerator(self
.filename
)
333 getattr(codeBody
, 'generate%sCode' % kind
)(node
)
334 self
.emit('SET_LINENO', node
.lineno
)
335 for default
in node
.defaults
:
337 self
.emit('LOAD_CONST', codeBody
)
338 self
.emit('MAKE_FUNCTION', len(node
.defaults
))
340 def visitFunction(self
, node
):
341 self
._visitFuncOrLambda
(node
, 'Function')
342 self
.storeName(node
.name
)
345 def visitLambda(self
, node
):
346 node
.name
= '<lambda>'
347 node
.varargs
= node
.kwargs
= None
348 self
._visitFuncOrLambda
(node
, 'Lambda')
351 def visitCallFunc(self
, node
):
354 if hasattr(node
, 'lineno'):
355 self
.emit('SET_LINENO', node
.lineno
)
356 self
.visit(node
.node
)
357 for arg
in node
.args
:
359 if isinstance(arg
, ast
.Keyword
):
363 self
.emit('CALL_FUNCTION', kw
<< 8 | pos
)
366 def visitKeyword(self
, node
):
367 self
.emit('LOAD_CONST', node
.name
)
368 self
.visit(node
.expr
)
371 def visitIf(self
, node
):
373 for test
, suite
in node
.tests
:
374 if hasattr(test
, 'lineno'):
375 self
.emit('SET_LINENO', test
.lineno
)
377 print "warning", "no line number"
380 self
.emit('JUMP_IF_FALSE', dest
)
383 self
.emit('JUMP_FORWARD', after
)
384 dest
.bind(self
.code
.getCurInst())
387 self
.visit(node
.else_
)
388 after
.bind(self
.code
.getCurInst())
394 self
.emit('SETUP_LOOP', l
.extentAnchor
)
397 def finishLoop(self
):
399 i
= self
.code
.getCurInst()
400 l
.extentAnchor
.bind(self
.code
.getCurInst())
402 def visitFor(self
, node
):
406 self
.emit('SET_LINENO', node
.lineno
)
408 self
.visit(node
.list)
409 self
.visit(ast
.Const(0))
410 l
.startAnchor
.bind(self
.code
.getCurInst())
411 self
.emit('SET_LINENO', node
.lineno
)
412 self
.emit('FOR_LOOP', anchor
)
413 self
.visit(node
.assign
)
414 self
.visit(node
.body
)
415 self
.emit('JUMP_ABSOLUTE', l
.startAnchor
)
416 anchor
.bind(self
.code
.getCurInst())
417 self
.emit('POP_BLOCK')
419 self
.visit(node
.else_
)
423 def visitWhile(self
, node
):
424 self
.emit('SET_LINENO', node
.lineno
)
429 lElse
= l
.breakAnchor
430 l
.startAnchor
.bind(self
.code
.getCurInst())
431 if hasattr(node
.test
, 'lineno'):
432 self
.emit('SET_LINENO', node
.test
.lineno
)
433 self
.visit(node
.test
)
434 self
.emit('JUMP_IF_FALSE', lElse
)
436 self
.visit(node
.body
)
437 self
.emit('JUMP_ABSOLUTE', l
.startAnchor
)
438 # note that lElse may be an alias for l.breakAnchor
439 lElse
.bind(self
.code
.getCurInst())
441 self
.emit('POP_BLOCK')
443 self
.visit(node
.else_
)
447 def visitBreak(self
, node
):
449 raise SyntaxError, "'break' outside loop"
450 self
.emit('SET_LINENO', node
.lineno
)
451 self
.emit('BREAK_LOOP')
453 def visitContinue(self
, node
):
455 raise SyntaxError, "'continue' outside loop"
457 self
.emit('SET_LINENO', node
.lineno
)
458 self
.emit('JUMP_ABSOLUTE', l
.startAnchor
)
460 def visitTryExcept(self
, node
):
461 # XXX need to figure out exactly what is on the stack when an
462 # exception is raised and the first handler is checked
463 handlers
= StackRef()
469 self
.emit('SET_LINENO', node
.lineno
)
470 self
.emit('SETUP_EXCEPT', handlers
)
471 self
.visit(node
.body
)
472 self
.emit('POP_BLOCK')
473 self
.emit('JUMP_FORWARD', lElse
)
474 handlers
.bind(self
.code
.getCurInst())
476 last
= len(node
.handlers
) - 1
477 for i
in range(len(node
.handlers
)):
478 expr
, target
, body
= node
.handlers
[i
]
479 if hasattr(expr
, 'lineno'):
480 self
.emit('SET_LINENO', expr
.lineno
)
484 self
.emit('COMPARE_OP', "exception match")
486 self
.emit('JUMP_IF_FALSE', next
)
495 self
.emit('JUMP_FORWARD', end
)
497 next
.bind(self
.code
.getCurInst())
499 self
.emit('END_FINALLY')
501 lElse
.bind(self
.code
.getCurInst())
502 self
.visit(node
.else_
)
503 end
.bind(self
.code
.getCurInst())
506 def visitTryFinally(self
, node
):
508 self
.emit('SET_LINENO', node
.lineno
)
509 self
.emit('SETUP_FINALLY', final
)
510 self
.visit(node
.body
)
511 self
.emit('POP_BLOCK')
512 self
.emit('LOAD_CONST', None)
513 final
.bind(self
.code
.getCurInst())
514 self
.visit(node
.final
)
515 self
.emit('END_FINALLY')
518 def visitCompare(self
, node
):
519 """Comment from compile.c follows:
521 The following code is generated for all but the last
522 comparison in a chain:
524 label: on stack: opcode: jump to:
530 b, 0-or-1 JUMP_IF_FALSE L1
534 We are now ready to repeat this sequence for the next
535 comparison in the chain.
537 For the last we generate:
543 If there were any jumps to L1 (i.e., there was more than one
544 comparison), we generate:
546 0-or-1 JUMP_FORWARD L2
552 self
.visit(node
.expr
)
553 # if refs are never emitted, subsequent bind call has no effect
556 for op
, code
in node
.ops
[:-1]:
557 # emit every comparison except the last
560 self
.emit('ROT_THREE')
561 self
.emit('COMPARE_OP', op
)
562 # dupTop and compareOp cancel stack effect
563 self
.emit('JUMP_IF_FALSE', l1
)
566 # emit the last comparison
567 op
, code
= node
.ops
[-1]
569 self
.emit('COMPARE_OP', op
)
570 if len(node
.ops
) > 1:
571 self
.emit('JUMP_FORWARD', l2
)
572 l1
.bind(self
.code
.getCurInst())
575 l2
.bind(self
.code
.getCurInst())
578 def visitGetattr(self
, node
):
579 self
.visit(node
.expr
)
580 self
.emit('LOAD_ATTR', node
.attrname
)
583 def visitSubscript(self
, node
):
584 self
.visit(node
.expr
)
585 for sub
in node
.subs
:
587 if len(node
.subs
) > 1:
588 self
.emit('BUILD_TUPLE', len(node
.subs
))
589 if node
.flags
== 'OP_APPLY':
590 self
.emit('BINARY_SUBSCR')
591 elif node
.flags
== 'OP_ASSIGN':
592 self
.emit('STORE_SUBSCR')
593 elif node
.flags
== 'OP_DELETE':
594 self
.emit('DELETE_SUBSCR')
597 def visitSlice(self
, node
):
598 self
.visit(node
.expr
)
601 self
.visit(node
.lower
)
604 self
.visit(node
.upper
)
606 if node
.flags
== 'OP_APPLY':
607 self
.emit('SLICE+%d' % slice)
608 elif node
.flags
== 'OP_ASSIGN':
609 self
.emit('STORE_SLICE+%d' % slice)
610 elif node
.flags
== 'OP_DELETE':
611 self
.emit('DELETE_SLICE+%d' % slice)
613 print "weird slice", node
.flags
617 def visitSliceobj(self
, node
):
618 for child
in node
.nodes
:
621 self
.emit('BUILD_SLICE', len(node
.nodes
))
624 def visitAssign(self
, node
):
625 self
.emit('SET_LINENO', node
.lineno
)
626 self
.visit(node
.expr
)
627 dups
= len(node
.nodes
) - 1
628 for i
in range(len(node
.nodes
)):
632 if isinstance(elt
, ast
.Node
):
636 def visitAssName(self
, node
):
637 # XXX handle OP_DELETE
638 if node
.flags
!= 'OP_ASSIGN':
639 print "oops", node
.flags
640 self
.storeName(node
.name
)
642 def visitAssAttr(self
, node
):
643 self
.visit(node
.expr
)
644 if node
.flags
== 'OP_ASSIGN':
645 self
.emit('STORE_ATTR', node
.attrname
)
646 elif node
.flags
== 'OP_DELETE':
647 self
.emit('DELETE_ATTR', node
.attrname
)
649 print "warning: unexpected flags:", node
.flags
653 def visitAssTuple(self
, node
):
654 self
.emit('UNPACK_TUPLE', len(node
.nodes
))
655 for child
in node
.nodes
:
659 visitAssList
= visitAssTuple
661 def binaryOp(self
, node
, op
):
662 self
.visit(node
.left
)
663 self
.visit(node
.right
)
667 def unaryOp(self
, node
, op
):
668 self
.visit(node
.expr
)
672 def visitAdd(self
, node
):
673 return self
.binaryOp(node
, 'BINARY_ADD')
675 def visitSub(self
, node
):
676 return self
.binaryOp(node
, 'BINARY_SUBTRACT')
678 def visitMul(self
, node
):
679 return self
.binaryOp(node
, 'BINARY_MULTIPLY')
681 def visitDiv(self
, node
):
682 return self
.binaryOp(node
, 'BINARY_DIVIDE')
684 def visitMod(self
, node
):
685 return self
.binaryOp(node
, 'BINARY_MODULO')
687 def visitPower(self
, node
):
688 return self
.binaryOp(node
, 'BINARY_POWER')
690 def visitLeftShift(self
, node
):
691 return self
.binaryOp(node
, 'BINARY_LSHIFT')
693 def visitRightShift(self
, node
):
694 return self
.binaryOp(node
, 'BINARY_RSHIFT')
696 def visitInvert(self
, node
):
697 return self
.unaryOp(node
, 'UNARY_INVERT')
699 def visitUnarySub(self
, node
):
700 return self
.unaryOp(node
, 'UNARY_NEGATIVE')
702 def visitUnaryAdd(self
, node
):
703 return self
.unaryOp(node
, 'UNARY_POSITIVE')
705 def visitUnaryInvert(self
, node
):
706 return self
.unaryOp(node
, 'UNARY_INVERT')
708 def visitNot(self
, node
):
709 return self
.unaryOp(node
, 'UNARY_NOT')
711 def visitBackquote(self
, node
):
712 return self
.unaryOp(node
, 'UNARY_CONVERT')
714 def bitOp(self
, nodes
, op
):
716 for node
in nodes
[1:]:
721 def visitBitand(self
, node
):
722 return self
.bitOp(node
.nodes
, 'BINARY_AND')
724 def visitBitor(self
, node
):
725 return self
.bitOp(node
.nodes
, 'BINARY_OR')
727 def visitBitxor(self
, node
):
728 return self
.bitOp(node
.nodes
, 'BINARY_XOR')
730 def visitTest(self
, node
, jump
):
732 for child
in node
.nodes
[:-1]:
736 self
.visit(node
.nodes
[-1])
737 end
.bind(self
.code
.getCurInst())
740 def visitAssert(self
, node
):
741 # XXX __debug__ and AssertionError appear to be special cases
742 # -- they are always loaded as globals even if there are local
743 # names. I guess this is a sort of renaming op.
745 self
.emit('SET_LINENO', node
.lineno
)
746 self
.emit('LOAD_GLOBAL', '__debug__')
747 self
.emit('JUMP_IF_FALSE', skip
)
749 self
.visit(node
.test
)
750 self
.emit('JUMP_IF_TRUE', skip
)
751 self
.emit('LOAD_GLOBAL', 'AssertionError')
752 self
.visit(node
.fail
)
753 self
.emit('RAISE_VARARGS', 2)
754 skip
.bind(self
.code
.getCurInst())
758 def visitAnd(self
, node
):
759 return self
.visitTest(node
, 'JUMP_IF_FALSE')
761 def visitOr(self
, node
):
762 return self
.visitTest(node
, 'JUMP_IF_TRUE')
764 def visitName(self
, node
):
765 self
.loadName(node
.name
)
767 def visitConst(self
, node
):
768 self
.emit('LOAD_CONST', node
.value
)
771 def visitEllipsis(self
, node
):
772 self
.emit('LOAD_CONST', Ellipsis)
775 def visitTuple(self
, node
):
776 for elt
in node
.nodes
:
778 self
.emit('BUILD_TUPLE', len(node
.nodes
))
781 def visitList(self
, node
):
782 for elt
in node
.nodes
:
784 self
.emit('BUILD_LIST', len(node
.nodes
))
787 def visitDict(self
, node
):
788 self
.emit('BUILD_MAP', 0)
789 for k
, v
in node
.items
:
790 # XXX need to add set lineno when there aren't constants
795 self
.emit('STORE_SUBSCR')
798 def visitReturn(self
, node
):
799 self
.emit('SET_LINENO', node
.lineno
)
800 self
.visit(node
.value
)
801 self
.emit('RETURN_VALUE')
804 def visitRaise(self
, node
):
805 self
.emit('SET_LINENO', node
.lineno
)
808 self
.visit(node
.expr1
)
811 self
.visit(node
.expr2
)
814 self
.visit(node
.expr3
)
816 self
.emit('RAISE_VARARGS', n
)
819 def visitPrint(self
, node
):
820 self
.emit('SET_LINENO', node
.lineno
)
821 for child
in node
.nodes
:
823 self
.emit('PRINT_ITEM')
826 def visitPrintnl(self
, node
):
827 self
.visitPrint(node
)
828 self
.emit('PRINT_NEWLINE')
831 def visitExec(self
, node
):
832 self
.visit(node
.expr
)
833 if node
.locals is None:
834 self
.emit('LOAD_CONST', None)
836 self
.visit(node
.locals)
837 if node
.globals is None:
840 self
.visit(node
.globals)
841 self
.emit('EXEC_STMT')
843 class LocalNameFinder
:
844 def __init__(self
, names
=()):
845 self
.names
= misc
.Set()
846 self
.globals = misc
.Set()
851 for elt
in self
.globals.items():
852 if self
.names
.has_elt(elt
):
853 self
.names
.remove(elt
)
856 def visitDict(self
, node
):
859 def visitGlobal(self
, node
):
860 for name
in node
.names
:
861 self
.globals.add(name
)
864 def visitFunction(self
, node
):
865 self
.names
.add(node
.name
)
868 def visitLambda(self
, node
):
871 def visitImport(self
, node
):
872 for name
in node
.names
:
875 def visitFrom(self
, node
):
876 for name
in node
.names
:
879 def visitClassdef(self
, node
):
880 self
.names
.add(node
.name
)
883 def visitAssName(self
, node
):
884 self
.names
.add(node
.name
)
888 self
.startAnchor
= StackRef()
889 self
.breakAnchor
= StackRef()
890 self
.extentAnchor
= StackRef()
892 class CompiledModule
:
893 """Store the code object for a compiled module
895 XXX Not clear how the code objects will be stored. Seems possible
896 that a single code attribute is sufficient, because it will
897 contains references to all the need code objects. That might be
900 MAGIC
= (20121 |
(ord('\r')<<16) |
(ord('\n')<<24))
902 def __init__(self
, source
, filename
):
904 self
.filename
= filename
907 t
= transformer
.Transformer()
908 self
.ast
= t
.parsesuite(self
.source
)
909 cg
= CodeGenerator(self
.filename
)
911 self
.code
= cg
.asConst()
913 def dump(self
, path
):
914 """create a .pyc file"""
916 f
.write(self
._pyc
_header
())
917 marshal
.dump(self
.code
, f
)
920 def _pyc_header(self
):
921 # compile.c uses marshal to write a long directly, with
922 # calling the interface that would also generate a 1-byte code
923 # to indicate the type of the value. simplest way to get the
924 # same effect is to call marshal and then skip the code.
925 magic
= marshal
.dumps(self
.MAGIC
)[1:]
926 mtime
= os
.stat(self
.filename
)[stat
.ST_MTIME
]
927 mtime
= struct
.pack('i', mtime
)
930 if __name__
== "__main__":
934 opts
, args
= getopt
.getopt(sys
.argv
[1:], 'vq')
938 ASTVisitor
.VERBOSE
= ASTVisitor
.VERBOSE
+ 1
940 f
= open('/dev/null', 'wb')
943 print "no files to compile"
945 for filename
in args
:
948 buf
= open(filename
).read()
949 mod
= CompiledModule(buf
, filename
)
951 mod
.dump(filename
+ 'c')