jsocol/bleach — Issue #46: Allow method chaining without rebuilding the tree
Author: jsocol
Created: 2011-10-21 16:48:38+00:00
State: open
Labels: clean, delinkify, linkify

Right now, if you chain methods (e.g. `linkify(clean(text))`) the whole tree-building, which is expensive, has to happen twice.

I can see two ways of solving this. One is a total API rewrite, one is less complicated. I'm leaning toward the latter.

**Total rewrite:**

Recreate a `Bleach` class, but this time it _is_ the text you want to clean, e.g.:

```python
from bleach import Bleach
text = Bleach(text)
print text.clean()
```

It could store state of what's already been done (like cleaning, linkifying, etc) and either return text (as above) or return itself to allow chaining, e.g.:

```python
# Implement __unicode__ and __str__, seems nicer than above.
print Bleach(text).clean().linkify()
```

**Less-crazy idea:**

Instead of actual `unicode` objects, the top-level methods return a lazy object that _looks_ like a string, maybe even descends from `unicode`, but is smarter, stores the tree, and only renders itself if anything needs to treat it as a string.

Then the methods would be reworked to check for the tree before rebuilding it, and if found, they would just use the existing tree. `clean()` might need special handling--i.e. always go back to text first--because of the nature of what it does. But I generally say you should always call `clean()` first if you're chaining, anyway.

**Pros and cons**

The rewrite might make it easier to deal with the increasing complexity of `linkify()`. You could (again) subclass `Bleach` and override a number of things there. I don't love APIs where you have to subclass things. I'm also not sure how complex this would make the upgrade for consumers--it would be a major version change.

The less-crazy idea doesn't involve subclassing anything, ever. The API can stay the same and it can be a minor version change. I am guessing won't be _too_ difficult to write some sort of `LazyTreeStringObjectThing` class. It would be a good opportunity to do some refactoring and get some stuff out of `bleach/__init__.py`. (I wouldn't change import paths, just where the code lives.)