[Doc-SIG] PythonPoint front-end
Aahz
aahz@pythoncraft.com
Fri, 25 Apr 2003 11:24:16 -0400
--0OAP2g/MAC+5xKAE
Content-Type: text/plain; charset=us-ascii
Content-Disposition: inline
On Fri, Apr 25, 2003, Patrick K. O'Brien wrote:
> Aahz <aahz@pythoncraft.com> writes:
>>
>> If you'd like, you can have my front-end to PythonPoint. (I created a
>> dead-simple half-reSTed syntax to avoid the drudgery of writing XML.)
>> Once Kevin does his work, it should be simple to modify my front-end to
>> write out reST.
>
> Sure, I'd like to take a look at that. Thanks. I also see that
> Richard Jones has a pythonpoint writer in his docutils sandbox. Has
> anyone used it?
Here you go. I'm including my PyCon objects presentation so you can see
how it works. (Haven't tried using Richard's bit; I wrote mine a year
ago when reST was really in the Dark Ages. ;-)
(It'll be available on my web site Real Soon Now....)
--
Aahz (aahz@pythoncraft.com) <*> http://www.pythoncraft.com/
"In many ways, it's a dull language, borrowing solid old concepts from
many other languages & styles: boring syntax, unsurprising semantics,
few automatic coercions, etc etc. But that's one of the things I like
about it." --Tim Peters on Python, 16 Sep 93
--0OAP2g/MAC+5xKAE
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment; filename="stx2xml.py"
import sys
import os
import re
import imp
import cgi
#cfg = imp.new_module('cfg')
class cfg: pass
tagDict = {}
class Tag:
closes = []
def __init__(self):
self._attr = {}
self.elements = []
def default(self, value):
pass
def end(self):
raise NotImplementedError
def output(self):
raise NotImplementedError
def get_attrs(self):
tmp = []
for key,value in self._attr.iteritems():
tmp.append('%s="%s"' % (key,value))
return ' '.join(tmp)
class Presentation(Tag):
def __init__(self):
Tag.__init__(self)
tagStack = []
def pragma_outputFile(self, value):
self.outputFile = value
def pragma_style(self, value):
mod, func = value.split(',')
self.styleMod, self.styleFunc = mod, func
f, p, d = imp.find_module(mod)
mod = imp.load_module(mod, f, p, d)
func = getattr(mod, func)
cfg.styles = func()
def pragma_frame(self, value):
cfg.pageFrame = tuple(value.split(','))
def pragma_footer(self, value):
cfg.footerFrame = tuple(value.split(','))
def pragma_codeRatio(self, value):
cfg.codeRatio = float(value)
def output(self):
self.setFooters()
tmp = []
tmp.append('<presentation %s>' % self.get_attrs())
mod, func = self.styleMod, self.styleFunc
tmp.append('<stylesheet module="%s" function="%s"/>' % (mod, func))
tmp.append('<section name="Main">')
for slide in self.elements:
tmp.append(slide.output())
tmp.append('</section>\n</presentation>')
return '\n\n'.join(tmp)
def setFooters(self):
numPages = len(self.elements)
pageNo = 0
for slide in self.elements:
pageNo += 1
slide.pageNo = pageNo
slide.numPages = numPages
tagDict['presentation'] = Presentation
class Para(Tag):
tagName = 'para'
styleName = None
reCode = re.compile("``([^`]+)``")
def __init__(self):
Tag.__init__(self)
self.codeSize = None
def getCodeSize(self):
fontSize = cfg.styles[self.styleName].fontSize
self.codeSize = round(fontSize * cfg.codeRatio, 1)
def parse(self, line):
if self.codeSize is None:
self.getCodeSize()
repl = r'<font name="Courier" size="%s">\1</font>' % self.codeSize
return self.reCode.sub(repl, line)
def default(self, value):
value = self.parse(value)
self.elements.append(value)
def end(self):
pass
def output(self):
outdict = {
'tagName': self.tagName,
'style': 'style="%s"' % self.styleName,
'data': '\n'.join(self.elements)
}
tmp = '<%(tagName)s %(style)s>%(data)s</%(tagName)s>' % outdict
return tmp
Para.closes = [Para]
class PreFmt(Para):
tagName = 'prefmt'
styleName = 'Normal'
tagDict['prefmt'] = PreFmt
class Slide(Tag):
def __init__(self):
Tag.__init__(self)
def pragma_title(self, value):
self._attr['title'] = value
tmp = Title()
tmp.default(value)
self.elements.append(tmp)
def pragma_head(self, value):
self._attr['title'] = value.replace("``", "")
tmp = Head()
tmp.default(value)
self.elements.append(tmp)
def end(self):
pass
def output(self):
tmp = []
tmp.append('<slide %s>' % self.get_attrs())
tmp.append(make_frame(cfg.pageFrame))
for tag in self.elements:
tmp.append(tag.output())
tmp.append('</frame>')
tmp.append(make_frame(cfg.footerFrame))
pageNo, numPages = self.pageNo, self.numPages
f = '<para style="Footer">%s</para>' % pageNo
tmp.append(f)
tmp.append('</frame>')
tmp.append('</slide>')
return '\n'.join(tmp)
Slide.closes = [Para, Slide]
tagDict['slide'] = Slide
class Title(Para):
styleName = 'title'
tmp = Title
tagDict[tmp.styleName] = tmp
class Head(Para):
styleName = 'head'
tmp = Head
tagDict[tmp.styleName] = tmp
class SmallTitle(Para):
styleName = 'smalltitle'
tmp = SmallTitle
tagDict[tmp.styleName] = tmp
class Normal(Para):
styleName = 'normal'
tmp = Normal
tagDict[tmp.styleName] = tmp
class Bullet(Para):
styleName = 'Bullet' # PythonPoint needs cap :-(
tagDict['bullet'] = Bullet
class Indent(Para):
styleName = 'indent'
tmp = Indent
tagDict[tmp.styleName] = tmp
class URL(Para):
styleName = 'url'
tmp = URL
tagDict[tmp.styleName] = tmp
class Code(PreFmt):
styleName = 'code'
def default(self, value):
value = cgi.escape(value)
PreFmt.default(self, value)
def pragma_include(self, value):
data = open(value).read()
data = cgi.escape(data)
self.elements.append(data)
tmp = Code
tagDict[tmp.styleName] = tmp
class Spacer(Para):
def output(self):
return '<spacer %s/>' % self.get_attrs()
tagDict['spacer'] = Spacer
class NumberedList(Para):
def default(self, value):
self.elements.append(value)
def output(self):
i = 1
tmp = []
for item in self.elements:
s = '<para style="NumList">%s. %s</para>' % (i, item)
tmp.append(s)
i += 1
return '\n'.join(tmp)
tagDict['numlist'] = NumberedList
def make_frame(frame):
return '<frame x="%s" y="%s" width="%s" height="%s">' % frame
def dispatch_tag(tag, root):
newTag = tagDict[tag]()
tagStack = root.tagStack
#print tag, newTag.closes
for tagType in newTag.closes:
currTag = tagStack[-1]
#print isinstance(currTag, tagType)
if isinstance(currTag, tagType):
currTag.end()
tagStack.pop()
tagStack[-1].elements.append(newTag)
tagStack.append(newTag)
#print tagStack
def dispatch_attrib(line, root):
currTag = root.tagStack[-1]
key, value = line.split(' ', 1)
currTag._attr[key] = value
def dispatch_pragma(line, root):
currTag = root.tagStack[-1]
pragma, value = line.split(' ', 1)
meth = getattr(currTag, 'pragma_' + pragma)
meth(value)
def dispatch_comment(line, root):
pass
dispatch = {
'.': dispatch_tag,
'=': dispatch_attrib,
'@': dispatch_pragma,
'#': dispatch_comment,
}
def parse(input):
root = Presentation()
root.tagStack = [root]
try:
for line in input:
line = line.rstrip()
if not line:
root.tagStack[-1].default(line)
continue
ch = line[0]
if ch in dispatch:
# handle continuation lines in interactive mode
if line[1] == '.':
root.tagStack[-1].default(line)
else:
dispatch[ch](line[1:], root)
else:
root.tagStack[-1].default(line)
except KeyError:
print line
raise
return root
def write(root):
f = open(root.outputFile, 'w')
f.write(root.output())
# print root.output()
def process(input):
input = open(input)
root = parse(input)
write(root)
if __name__ == '__main__':
input = sys.argv[1]
process(input)
--0OAP2g/MAC+5xKAE
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment; filename="pyobj.stx"
=filename pyobj.pdf
@outputFile pyobj.xml
@style style,Styles
@frame 0,0,782,612
@footer 0,5,787,25
@codeRatio 0.85
.slide
@title Python Objects and New-style Classes
.spacer
=height 40
.smalltitle
<font size="60">Aahz</font>
.spacer
=height 20
.smalltitle
``aahz@pythoncraft.com``
.smalltitle
``http://pythoncraft.com/``
.spacer
=height 60
.smalltitle
Powered by PythonPoint
.spacer
=height 10
.smalltitle
``http://www.reportlab.com/``
.slide
=outlinelevel 1
@head Before we begin...
.bullet
I'm hearing-impaired
.indent
Please speak slowly and clearly
.slide
@head Overall
.bullet
Goal: understand objects
.indent
Use new-style classes to illuminate
.url
http://www.python.org/2.2.2/descrintro.html
.indent
No classic classes
.bullet
Timing
.indent
30 minutes core material
.indent
15 minutes optional material
.indent
# Deal with stupid ReportLab entity bug
15-30 minutes Q&amp;A
.slide
@head Contents
.bullet
Object Basics
.bullet
Type/class tangle
.bullet
Memory management
.bullet
Optional Material
.indent
Mutable/immutable
.slide
@head Object Basics
.bullet
Objects
.bullet
Targets
.bullet
Namespace/Scope
.slide
=outlinelevel 1
@head Objects Defined
.bullet
Object is structured memory
.bullet
Object has:
.indent
Type
.indent
Namespace (attributes)
.indent
Value (optional: int/string objects have value, files don't)
.slide
=outlinelevel 1
@head Everything an Object
.bullet
All data is object
.indent
Functions, types, classes, class instances, iterators, generators,
modules, and files all first-class objects
.bullet
Capabilities
.indent
Objects all have same potential capabilities, but objects
choose which capabilities to expose.
.indent
The type determines the capabilities.
.indent
("Capability" in its generic sense, essentially
"set of data/methods" -- not the python-dev thread. ;-)
.slide
=outlinelevel 1
@head Accessing objects
.bullet
Targets contain bindings to objects
.indent
"Binding" used instead of "reference" because you can only deal with
objects directly, never the references themselves (except in C API).
.slide
=outlinelevel 1
@head Kinds of Targets
.bullet
Names
.bullet
Attributes
.indent
Names in object space, accessed with dot notation
.bullet
Computed
.indent
Indexes / Keys
.bullet
Anonymous
.indent
E.g. ``return`` or function defaults
.indent
Important for understanding refcounts
.slide
=outlinelevel 1
@head Names
.bullet
What's a name?
.indent
Bare word at builtin, module global, or function scope (more on scope
shortly)
.bullet
"Name" instead of "variable"
.indent
Names in Python don't contain data, only bindings; they're used like
variables, mostly, but use "names" instead to remember the semantics
.slide
=outlinelevel 1
@head Each Object a Namespace
.bullet
Attributes
.indent
Objects contain dict that maps key/value pairs to names and objects.
For some objects (primarily built-in types, function locals, and some
new-style classes), dict gets mapped to vector for reduced memory
consumption and speed.
.slide
=outlinelevel 1
@head Binding Operations
.bullet
Creating names
.indent
``=``, ``for`` (and list comprehensions), ``def``,
``import`` (all forms), ``class``, function/method parameters,
``except`` block (second parameter)
.bullet
Other targets
.indent
``=`` (sequences/maps), ``return``, ``yield``, ``print``,
function/method default parameters
.slide
=outlinelevel 1
@head Namespace Demo
.bullet
``namespace.py``
.slide
=outlinelevel 1
@head Scope
.bullet
Two scope hierarchies:
.indent
Execution scope
.indent
Class inheritance scope
.bullet
Read-only
.indent
Non-local scopes are read-only unless made explicit
.slide
=outlinelevel 1
@head Execution Scope
.bullet
Searches three namespaces in order:
.indent
Function local
.indent
Module global
.indent
Builtins
.bullet
Shadowing
.indent
Locals shadow globals shadow builtins
.slide
=outlinelevel 1
@head Nested Scopes
.bullet
Functions inside functions
.bullet
Lexical scope
.indent
Not dynamic scope
.indent
Allows static analysis of source code
.bullet
Python 2.1
.indent
``from __future__ import nested_scopes``
.slide
=outlinelevel 1
@head Class Inheritance Scope
.bullet
Non-standard "scope" usage
.indent
Some people disagree with me that class attribute/method inheritance
should be discussed as a "scope", but it's a convenient way to explain
the similarities with execution scope
.bullet
``self``
.indent
Used in methods to distinguish between execution scope (implicit as
in other functions) and the class inheritance scope (which is made
explicit through ``self``)
.slide
=outlinelevel 1
@head Scope Demo
.bullet
``scope.py``
.slide
@head Type/Class Tangle
.bullet
Inheritance vs. creation
.bullet
Class vs. instance
.slide
=outlinelevel 1
@head Inheritance vs. creation
.bullet
Inheritance
.indent
Attribute search path
.bullet
Type
.indent
Types are objects that create other objects
.indent
Metaclass is type that generates classes; class is type that
generates class instances
.indent
``type(Foo) == Foo.__class__``
.bullet
Instance
.indent
Instance is an object created by a type
.slide
=outlinelevel 1
@head Class vs. Instance
.bullet
Instance attribute
.indent
Access with ``self``
.bullet
Class attribute
.indent
Access with class name
.indent
But remember that a class is also an instance...
.slide
=outlinelevel 1
@head More Inheritance
.bullet
Bases vs. Types
.indent
An instance's inheritance searches its type, then its type's base
classes. Inheritance does NOT search the type's type.
.slide
=outlinelevel 1
@head Type/Class Demo
.bullet
``typeclass.py``
.slide
@head Memory Management
.bullet
Reference counts
.bullet
Garbage collection
.bullet
``__slots__``
.slide
=outlinelevel 1
@head Reference Counts
.bullet
Object deletion
.indent
Objects are deleted when no bindings reference object
.bullet
Binding
.indent
Each binding increases refcount
.bullet
Decrease refcount
.indent
Rebinding
.indent
Name going out of scope
.indent
``del``
.slide
=outlinelevel 1
@head ``del``
.bullet
Deletes bindings, not objects
.bullet
Can ``del`` parts of objects:
.spacer
=height 10
.code
>>> L = [1,2,3,4,5]
>>> del L[1:3]
>>> L
[1, 4, 5]
>>> d = {'a': 1, 'b': 2, 'c': 5}
>>> del d['b']
>>> d
{'a': 1, 'c': 5}
.slide
=outlinelevel 1
@head Refcount Cascades
.bullet
Objects refer to other objects
.indent
When an object gets deleted, all objects it refers to get decremented and
may be deleted -- leading to a cascade of object deletions
.bullet
Large lists/dicts
.indent
Deleting a large list or dict can take a long time as each object in the
list/dict needs to be walked
.slide
=outlinelevel 1
@head GC
.bullet
Garbage collection
.indent
In addition to refcount, not instead of
.bullet
Cycles
.indent
Occur when two or more objects refer to each other; refcount
can't go to zero. If no external targets refer to cycle,
cycle is "garbage" and GC deletes it.
.bullet
``__del__()``
.indent
Objects with ``__del__()`` can't be GC'd because of ordering problem
.slide
=outlinelevel 1
@head Refcount/GC Demo
.bullet
``refcount_gc.py``
.slide
=outlinelevel 1
@head ``__slots__``
.bullet
Attributes
.indent
Normally stored in dict
.indent
``__slots__`` creates indexed vector
.indent
Attribute names stored with class, not instance
.slide
@head Mutable vs. Immutable
.bullet
Immutable objects
.indent
Strings, numbers, tuples, some class instances, and combinations
.bullet
Mutable objects
.indent
Lists, dicts, most classes and class instances
.bullet
Why immutable?
.indent
Dict keys, space efficiency
.slide
=outlinelevel 1
@head Mutable Subtleties
.bullet
Lists embedded in tuples
.spacer
=height 10
.code
>>> a = (1, ['foo'], 's')
>>> a[1] = {'foo': 'bar'}
Traceback (most recent call last):
File "<stdin>", line 1, in ?
TypeError: object doesn't support item assignment
>>> a[1].append('bar')
>>> a
(1, ['foo', 'bar'], 's')
.spacer
=height 5
.bullet
Classes
.indent
Cannot force class immutable; it's an implied contract in code and
documentation (must use C API to force immutable)
.slide
=outlinelevel 1
@head Copy Semantics (for function calls)
.bullet
Immutables don't need copy
.bullet
Mutables must be copied
.indent
If you don't want them modified, that is
.slide
=outlinelevel 1
@head Copy Example
.spacer
=height 15
.normal
``list.sort()`` is an in-place operation; here we create a function that
returns the list
.spacer
=height 10
.code
>>> def sort(L):
... L.sort()
... return L
...
>>> L = [11, 7, 12, 5]
>>> sort(L[:])
[5, 7, 11, 12]
>>> L
[11, 7, 12, 5]
>>> sort(L)
[5, 7, 11, 12]
>>> L
[5, 7, 11, 12]
.spacer
=height 15
.normal
Notice how failing to make copy modifies original list
.slide
=outlinelevel 1
@head Mutability Again
.bullet
``=`` vs. ``+=`` (and family)
.indent
``=`` always [re]binds
.indent
mutable vs. immutable
.indent
``+=`` may rebind ``self`` to original object if mutable (but not
required)
.slide
=outlinelevel 1
@head Tuples and ``+=``
.spacer
=height 10
.code
>>> a = (1, ['foo'], 'xyzzy')
>>> a[1].append('bar')
>>> a
(1, ['foo', 'bar'], 'xyzzy')
>>> a[1] = 9
Traceback (most recent call last):
File "<stdin>", line 1, in ?
TypeError: object doesn't support item assignment
>>> a
(1, ['foo', 'bar'], 'xyzzy')
>>> a[1] += ['spam']
Traceback (most recent call last):
File "<stdin>", line 1, in ?
TypeError: object doesn't support item assignment
>>> a
(1, ['foo', 'bar', 'spam'], 'xyzzy')
.slide
=outlinelevel 1
@head ``+=`` expansion
.spacer
=height 10
.code
a.__setitem__(1,
a.__getitem__(1).__iadd__(['spam']))
.spacer
=height 15
.normal
Rewrite with temp vars:
.spacer
=height 10
.code
>>> a = (1, ['foo', 'bar'], 'xyzzy')
>>> x = a.__getitem__(1)
>>> x
['foo', 'bar']
>>> x.__iadd__(['spam'])
['foo', 'bar', 'spam']
>>> a.__setitem__(1, x)
Traceback (most recent call last):
File "<stdin>", line 1, in ?
AttributeError: 'tuple' object has
no attribute '__setitem__'
>>> a
(1, ['foo', 'bar', 'spam'], 'xyzzy')
--0OAP2g/MAC+5xKAE--