From: Yuri T. <qar...@gm...> - 2008-11-14 09:44:41
|
Now that we are changing so much, I want to propose a change that has been on my mind for quite some time. Back in the day I made a decision to implement all processors as classes. Since then I've been thinking that perhaps implementing them simply as functions would make things a lot easier and intuitive. It may make some complicated cases a little more complicated, but will simplify the simple cases, which would make it easier for people to write simple extensions. For instance, we would have def handleFootnotePattern(md, match) : sup = etree.Element("sup") a = etree.SubElement(sup, "a") id = m.group(2) sup.set('id', md.footnotes.makeFootnoteRefId(id)) a.set('href', '#' + md.footnotes.makeFootnoteId(id)) a.set('rel', 'footnote') a.text = str(md.footnotes.footnotes.index(id) + 1) return sup instead of: class FootnotePattern(markdown.Pattern): """ InlinePattern for footnote markers in a document's body text. """ def __init__(self, pattern, footnotes): markdown.Pattern.__init__(self, pattern) self.footnotes = footnotes def handleMatch(self, m): sup = etree.Element("sup") a = etree.SubElement(sup, "a") id = m.group(2) sup.set('id', self.footnotes.makeFootnoteRefId(id)) a.set('href', '#' + self.footnotes.makeFootnoteId(id)) a.set('rel', 'footnote') a.text = str(self.footnotes.footnotes.index(id) + 1) return sup and then: md.inlinePatterns.add("footnote", (FOOTNOTE_RE, handleFootnotePattern), "<reference") instead of: md.inlinePatterns.add("footnote", FootnotePattern(FOOTNOTE_RE, self), "<reference") If we do this, I suggest that we do this for _all_ types of processors. I don't think this would be too complicated to implement. - yuri -- http://sputnik.freewisdom.org/ |
From: Waylan L. <wa...@gm...> - 2008-11-14 14:44:59
|
On Fri, Nov 14, 2008 at 4:44 AM, Yuri Takhteyev <qar...@gm...> wrote: > Now that we are changing so much, I want to propose a change that has > been on my mind for quite some time. Back in the day I made a > decision to implement all processors as classes. Since then I've been > thinking that perhaps implementing them simply as functions would make > things a lot easier and intuitive. It may make some complicated cases > a little more complicated, but will simplify the simple cases, which Meh. I'v thought about this before and even wondered why you used classes back when I started my first extension, but the class gives us so much more. Look at the InlinePatterns. Many of them are subclasses that override only part of the functionality. In fact, a few don't even have a run method, they just override an auxiliary method. Actually, there are quite a few auxiliary methods that would now litter the markdown namespace unnecessarily. Using classes keeps things neat, compartmentalized, and DRY. On a related note, when I first started to write the new BlockParser, I was intending to use functions rather than classes for the processors, but it quickly became clear that classes were a much better solution. And then there are a few extensions in which I use the class to remember state. That wouldn't work with a function. Yeah, we can always keep state on the Markdown instance, but if the only place we use that state is within one processor, why litter the Markdown instance? Personally, if you made this change, I would still be inclined to use classes with the ``__call__`` method in place of the current ``run`` method to simulate a function. But even then, as callables can't be instances, I couldn't override ``__init__`` to pass in extra arguments and/or set state on init. > would make it easier for people to write simple extensions. I agree this is important, but I think the answer is good documentation. Have you read my documentation [1] for writing extensions (the core parser part needs updating)? Once it's all spelled out, it doesn't seem that hard IMO. [1]: http://gitorious.org/projects/python-markdown/repos/mainline/blobs/master/docs/writing_extensions.txt -- ---- Waylan Limberg wa...@gm... |
From: Waylan L. <wa...@gm...> - 2008-11-14 15:26:44
|
On Fri, Nov 14, 2008 at 9:44 AM, Waylan Limberg <wa...@gm...> wrote: [snip] > method to simulate a function. But even then, as callables can't be > instances, I couldn't override ``__init__`` to pass in extra arguments > and/or set state on init. > Sorry, I misspoke here. Actually ``__call__`` only works with an instance of a class, so this would work as the instance needs to be initialized first. But, I think that would be more confusing than the current way of doing things to the beginner. -- ---- Waylan Limberg wa...@gm... |
From: Yuri T. <qar...@gm...> - 2008-11-17 07:18:04
|
> Meh. I'v thought about this before and even wondered why you used > classes back when I started my first extension, but the class gives us > so much more. Look at the InlinePatterns. Many of them are subclasses > that override only part of the functionality. In fact, a few don't > even have a run method, they just override an auxiliary method. > Actually, there are quite a few auxiliary methods that would now > litter the markdown namespace unnecessarily. Using classes keeps > things neat, compartmentalized, and DRY. Ok, a variation on this idea: each odict would be allowed to take as entries any callable objects - either a function or an instance of a callable class would do. We can stick with classes for our implementations, but let the users extend the functionality by adding either instances or functions. - yuri -- http://sputnik.freewisdom.org/ |
From: Waylan L. <wa...@gm...> - 2008-11-17 14:56:05
|
On Mon, Nov 17, 2008 at 2:17 AM, Yuri Takhteyev <qar...@gm...> wrote: >> Meh. I'v thought about this before and even wondered why you used >> classes back when I started my first extension, but the class gives us >> so much more. Look at the InlinePatterns. Many of them are subclasses >> that override only part of the functionality. In fact, a few don't >> even have a run method, they just override an auxiliary method. >> Actually, there are quite a few auxiliary methods that would now >> litter the markdown namespace unnecessarily. Using classes keeps >> things neat, compartmentalized, and DRY. > > Ok, a variation on this idea: each odict would be allowed to take as > entries any callable objects - either a function or an instance of a > callable class would do. We can stick with classes for our > implementations, but let the users extend the functionality by adding > either instances or functions. > That would be a simple change - just rename all 'run' methods to '__call__' in the code and adjust the calling code accordingly. I have some reservations[^1], but also see it as slightly more pythonic. Whatever. If we did do this, I would suggest adding a utility function to add a regex pattern to a function: def makePattern(regex, callable): return callable.pattern = regex That may not be exactly what we want, but you get the idea. Then, one could do: md.inlinePatterns.add("footnote", makePattern(FOOTNOTE_RE, handleFootnotePattern), "<reference") Of course, this would not be necessary for the callable classes (as they already do this internally), just functions. But, that way the odict would only ever contain one object each (no tuples), and the regex would still be mapped to the pattern/processor. [^1]: My concern is that some newcomers to python may see the callable classes as a higher barrier of entry as they aren't up on the interworkings of python. That also means that our documentation may have to spend at least a little time educating people about python itself. That said, it is the python way, so it can't be all that bad. -- ---- Waylan Limberg wa...@gm... |