springload / draftjs_exporter Goto Github PK
View Code? Open in Web Editor NEWConvert Draft.js ContentState to HTML
Home Page: https://www.draftail.org/blog/2018/03/13/rethinking-rich-text-pipelines-with-draft-js
License: MIT License
Convert Draft.js ContentState to HTML
Home Page: https://www.draftail.org/blog/2018/03/13/rethinking-rich-text-pipelines-with-draft-js
License: MIT License
Raised by @loicteixeira in https://github.com/springload/draftjs_exporter/pull/74/files#r135967185. Opening as a separate issue because it's worth discussing and there is no point in holding that PR for this.
At the moment, draftjs_exporter
defines its dependencies as:
Lines 15 to 22 in c963514
Those ranges are purposefully big (we want to support as many versions as possible for something as fundamental to people's tech stacks), which is good, but as @loicteixeira puts it then it would make sense to test accordingly, with the lower and upper bounds at least.
More info to help in the decision,
setup.py
to limit installed versions to below 4.0.0? "beautifulsoup4>=4.5.1",
"html5lib>=0.999,<1",
Finally, bear in mind that our usage of the APIs of those dependencies is very small (HTML string -> DOM nodes conversion, DOM nodes -> HTML string conversion, create nodes, append child to node), which means that the potential breakage would only likely be in how those engines handle specific content, which is hard to test for. We do however have a small test suite of "potential engine quirks": https://github.com/springload/draftjs_exporter/blob/master/tests/engines/test_engines_differences.py
Follow-up to #87. At the moment, entity components are only given a small amount of data about an entity for rendering:
draftjs_exporter/draftjs_exporter/entity_state.py
Lines 48 to 58 in 764d377
There are a few more problems here:
entityMap
.data
is given as the top-level props, which makes it impossible to add extra props without risking overwriting the entity data (say if the entity has a field called type
that does something different from the entity type).We should refactor this to something like:
props = {}
props['entity'] = entity_details
props['entity']['key'] = key
props['block'] = block
props['blocks'] = blocks
# (Potentially the `entity_range` as well?)
This way, it's easy to add arbitrary props without risking overwrites. The components get full access to the entity data, mutability, type, and key. And to the current block and blocks list, like styles, decorators, blocks.
Compared to the changes introduced in #90, this would be a breaking change, so should be considered carefully. Users of the exporter will have to rewrite their renderers slightly to use the new props shape.
I began to use draft-js and I discovered a feature called 'block-level data'. Via Modifier#setBlockData you can set additional metadata for a specific block.
In that case the generated JSON looks like this:
{
"entityMap": {},
"blocks": [
...
{
"key": "a4dpo",
"text": "asdfasdfsadfatsetsd",
"type": "direct-speech",
"depth": 0,
"inlineStyleRanges": [],
"entityRanges": [],
"data": {
"name": "test"
}
},
...
]
}
Is there any way to implement a block rendering depending on these attributes values?
Using create-react-app and flask for example.
Deployed on Heroku
Infringing test case:
draftjs_exporter/tests/test_output.py
Line 497 in d84f436
[
{
'text': 'Backtracking, two at once... (2)',
'depth': 2,
},
{
'text': 'Uh oh (1)',
'depth': 1,
},
{
'text': 'Up, up, and away! (2)',
'depth': 2,
},
{
'text': 'Arh! (1)',
'depth': 1,
},
{
'text': 'Did this work? (0)',
'depth': 0,
},
]
Should be :
Is (C is not in the right spot):
Hi there,
I'm working on integrating this library in my own project, it works really nice, great work, thanks a lot.
And is there a plan to support the decorator rendering? It'll be quite useful.
Edit: π§ For hash issues since the package got published as a wheel β see comments below.
It looks like the exporter isnβt using wheels as its published format. We should switch over to wheels, which have a number of advantages as described on https://pythonwheels.com/.
Itβs not clear to me whether switching from eggs to wheels is a breaking change or not, so the first step would be to research this and decide what to do.
becomes:
<ul class="bullet-list"><li>List item<ul class="bullet-list"><li>Nested list item</li></ul></li></ul>
<ul class="bullet-list"><li>De-nested list item</li></ul>
should be a single ul (same wrapper) with two depth 0 items.
Gut feel code to investigate: https://github.com/springload/draftjs_exporter/blob/master/draftjs_exporter/wrapper_state.py#L114
There is no plans to do this immediately, just opening this early so people interested in this project have a fair heads-up.
I'm looking into Python versions support of the exporter as part of #101. Although that issue isn't anything like a pressing feature / bug to address, I think it's important to be clear that the exporter isn't going to support old versions of Python forever.
Python 2.7 will officially stop being supported as a language on January 1st 2020 (PEP-373), 8 months from now (https://pythonclock.org/). The exporter should also drop Python 2.7 support then, if not before, like many other projects (https://python3statement.org/). The exporter is relatively stable, so there is no need to do this sooner than needed, either in 2020 to align with other projects in the Python ecosystem, or earlier if there is important work done on the exporter and it feels like Python 2.7 support is a hindrance.
There are many people relying on this project and using Python 2.7 at the moment, here are the pip
installs over the last 30 days:
| python_version | download_count |
| -------------- | -------------- |
| 3.6 | 18,249 |
| 2.7 | 11,815 |
| 3.7 | 8,106 |
| 3.5 | 3,112 |
| 3.4 | 384 |
| 3.3 | 4 |
| 3.8 | 3 |
| 2.6 | 1 |
| Total | 41,674 |
If you're one of them, it would be good to hear from you! Please be assured that if upgrading Python versions isn't an option for you, older versions of the exporter will still be available and keep on working. Considering how stable this project is, they'll also most likely keep on being relevant.
At the moment the exports
test suite is used to measure performance. While the content in it is significant, it's not really representative of real-world workloads.
Once someone has an open-source site using this, I would be keen to dump all of its content in this repository so we have more relevant numbers to look at.
First of all, I would like to thank you as someone who is using this library.
I'm using this library with a markdown version. (draftjs_exporter_markdown)
Because the dom of draftjs_exporter.dom.DOM is a class-level variable, it could be a problem depending on the timing of creating two versions of the HTML object.
Furthermore, I'm using this on a web server, this can cause bigger problems in a multithreaded environment.
Not only the markdown version, but the 3 engines built-in draftjs_exporter will cause the same problem.
Could you solve this problem?
Attached test code and results.
from draftjs_exporter.dom import DOM
from draftjs_exporter.html import HTML
from draftjs_exporter.constants import ENTITY_TYPES
from draftjs_exporter_markdown import ENGINE as MARKDOWN_ENGINE
template = {
'blocks': [
{'key': 'rrwx',
'type': 'unstyled',
'text': '<a href="https://www.google.com">google.com</a>',
'depth': 0,
'inlineStyleRanges': [],
'entityRanges': [{'offset': 9, 'length': 22, 'key': 0}],
'data': {}}],
'entityMap': {'0': {'type': 'LINK',
'mutability': 'MUTABLE',
'data': {'url': 'https://www.google.com'}}}}
html_exporter = HTML({
'entity_decorators': {
ENTITY_TYPES.LINK: lambda props: DOM.create_element('a', {
'href': props['url']
}, props['children']),
},
'engine': DOM.LXML
})
html1 = html_exporter.render(template)
markdown_exporter = HTML({
'engine': MARKDOWN_ENGINE
})
html2 = html_exporter.render(template)
print(html1)
print(html2)
output:
<p><a href="<a href="https://www.google.com">https://www.google.com</a>">google.com</a></p>
<p><a href="<a href="https://www.google.com">https://www.google.com</a>">google.com</a></p>
Copy/pasting @loicteixeira's point about the current exceptions:
Something to consider as well is whether the renderer should crash when there is a component it doesnβt know about it, or if it should ignore it? Maybe a settings in
draftjs_exporter
likeRAISE_MISSING_BLOCK_ERRORS
which we will probably set to the same value asDEBUG
.
At the moment, style_map
components do not receive any data beyond the text to style (as props['children]
).
draftjs_exporter/draftjs_exporter/style_state.py
Lines 26 to 32 in 209631a
This is ok for common use cases (BOLD
, ITALIC
, etc), but it makes the style_map fallback rather useless βΒ there is no way to know what style needs the fallback, or have any other information about the context to adjust the fallback behavior.
Here's what the block_map
fallback has access to for comparison:
props['block'] = {
'type': type_,
'depth': depth,
'data': data,
}
In retrospect I think this could've been all of the block's attributes, not just a cherry-picked shortlist, so for inline styles we could pass the following exhaustive props
:
{
# The style range to render.
"range":
"offset": 10,
"length": 17,
"style": "BOLD"
},
# The full block data, eg.
"block": {
"key": "t7k7",
"text": "Unstyled test test test test test",
"type": "unstyled",
"depth": 0,
"inlineStyleRanges": [
{
"offset": 10,
"length": 17,
"style": "BOLD"
}
],
"entityRanges": [
{
"offset": 0,
"length": 4,
"key": 6
}
],
"data": {}
},
}
Here's the approximative change:
- def render_styles(self, text_node):
+ def render_styles(self, text_node, block):
node = text_node
if not self.is_empty():
# Nest the tags.
for s in sorted(self.styles, reverse=True):
opt = Options.for_style(self.style_map, s)
+ props['block'] = block
+ props['range'] = s
node = DOM.create_element(opt.element, opt.props, node)
return node
Ideally I'd like entities and blocks to also be given more data (enough data to recreate the whole ContentState, thus making the exporter usable to create content migrations), but that's a separate issue.
I do not think there is a reason for using BeautifulSoup on this project, apart from the API being documented.
Presumably, using html5lib
's tree builder directly would result in performance gains. I'm not sure if there are tradeoffs.
"entityMap": {
"0": {
"type": "INLINE-EQUATION",
"mutability": "IMMUTABLE",
"data": {"text": "u=1/5, and v=3/5"}
}
}
I have tried something like this but its not working
"entity_decorators": {
"INLINE-EQUATION": lambda props: DOM.create_element(
"span", {"class": "mjx-process", 'data-inline-math': props['text']}
),
}
need to generate math HTML! can anyone help me with this ?
How to apply text align inline style inside header tag?
Example : block
{
"key": "cu1e2",
"data": {},
"text": "test",
"type": "header-three",
"depth": 0,
"entityRanges": [],
"inlineStyleRanges": [
{
"style": "right",
"length": 4,
"offset": 0
}
]
},
It would be handy to allow custom engines to be loaded via path (and not only by passing the class) with
django.utils.module_loading.import_string
.
So we don't tie this project to Django, here is the import_string
source code which only relies on six
.
Make a dependency-free implementation of DOMEngine
based on xml.etree.ElementTree
or xml.etree.cElementTree
. This might have a positive performance impact, as well as facilitating the use of the exporter.
For reference, here is my tentative implementation of an engine using ElementTree
. For some reason it outputs wrappers twice, I would suspect a bug in wrapper_state
that this particular implementation surfaces.
class DOM_ETREE(DOMEngine):
"""
lxml implementation of the DOM API.
"""
@staticmethod
def create_tag(type_, attr=None):
if not attr:
attr = {}
return etree.Element(type_, attrib=attr)
@staticmethod
def parse_html(markup):
pass
@staticmethod
def append_child(elt, child):
if hasattr(child, 'tag'):
elt.append(child)
else:
c = etree.Element('fragment')
c.text = child
elt.append(c)
@staticmethod
def render(elt):
return re.sub(r'(</?fragment>)', '', etree.tostring(elt, method='html'))
@staticmethod
def render_debug(elt):
return etree.tostring(elt, method='html')
Edit: once implemented, this could become the default engine so people can more easily "choose their own adventure" with any other engine, but still have a working default when doing pip install draftjs_exporter
.
At the moment the project is tested on Python 2.7, 3.4, 3.5. We need to update this to test Python 3.6 (or whatever the latest version is when we get to this task).
Also, I'm very keen on limiting the number of environments we test on. Would it be reasonable to:
Since Pyre got released, I've been thinking the exporter would be a good project to have type annotations in, either with Pyre or Mypy.
Corresponding PEPs:
Also worth checking out:
As far as I understand, in order to release anything useful, PEP-3107 is the bare minimum, and PEP-484 is a good baseline, so Python 3.5+ only. This means this package would need to drop support for Python 2.7 and 3.4. Wagtail, the main project relying on this package, has already dropped Python 2.7 compatibility, and the last version of Django to support Python 3.4 was v2.0 (https://docs.djangoproject.com/en/dev/faq/install/)
Edit: ^ I might be wrong, since the annotation syntax is supported starting with Python 3 the package should work in versions below 3.5. But type checking will only be doable starting in v3.5+?
This means that starting when Wagtail makes a new release without Django 2.0 support (or without Python 3.4 support, if that comes first), it will be possible to release the exporter with type annotations included (some time in 2019, see https://docs.wagtail.io/en/latest/releases/upgrading.html).
If anyone wants to experiment with this in the meantime, I would be interested to see what bugs this would surface. In my opinion the first step would be to use https://github.com/dropbox/pyannotate. I think there is a similar project from Google that does annotations based on instrumentation of running code.
pypi project page https://pypi.org/project/draftjs_exporter/ doesn't look nice, because pypi requires package readme in reStructuredText format not markdown
At the moment, the exporter loads the html5lib engine even if it is configured to use another one. The impact is 11.6MB of memory taken for nothing:
Line # Mem usage Increment Line Contents
================================================
8 11.8 MiB 0.0 MiB @profile
9 def run():
10 23.4 MiB 11.6 MiB from draftjs_exporter.html import HTML
11
12 23.4 MiB 0.0 MiB HTML({'engine': 'string'})
[...]
Small demo script:
from memory_profiler import profile
@profile
def run():
from draftjs_exporter.html import HTML
HTML({'engine': 'string'}).render({
'entityMap': {},
'blocks': [
{
"key": "2nols",
"text": "Test",
"type": "unstyled",
"depth": 0,
"inlineStyleRanges": [],
"entityRanges": [],
"data": {}
},
]
})
run()
This could be a relatively simple fix (see patch below) if DOM.use
was used consistently, but there are many tests that aren't calling it explicitly (and thus implicitly rely on the html5lib default). I would suggest to wait for #79 to be taken care of, so we don't have to update all those tests and they can then rely on the new default.
--- a/draftjs_exporter/dom.py
+++ b/draftjs_exporter/dom.py
@@ -3,7 +3,6 @@ from __future__ import absolute_import, unicode_literals
import inspect
import re
-from draftjs_exporter.engines.html5lib import DOM_HTML5LIB
from draftjs_exporter.error import ConfigException
# Python 2/3 unicode compatibility hack.
@@ -28,7 +27,7 @@ class DOM(object):
LXML = 'lxml'
STRING = 'string'
- dom = DOM_HTML5LIB
+ dom = HTML5LIB
@staticmethod
def camel_to_dash(camel_cased_str):
@@ -37,7 +36,7 @@ class DOM(object):
return dashed_case_str.replace('--', '-')
@classmethod
- def use(cls, engine=DOM_HTML5LIB):
+ def use(cls, engine=HTML5LIB):
"""
Choose which DOM implementation to use.
"""
@@ -45,6 +44,7 @@ class DOM(object):
if inspect.isclass(engine):
cls.dom = engine
elif engine.lower() == cls.HTML5LIB:
+ from draftjs_exporter.engines.html5lib import DOM_HTML5LIB
cls.dom = DOM_HTML5LIB
elif engine.lower() == cls.LXML:
pip 10.0.1 from /usr/local/lib/python3.5/dist-packages/pip (python 3.5)
Downloading https://files.pythonhosted.org/packages/6d/e5/fcf88a5dab82ca619ff6824a062b46d9315ba91e64204275213a1a712125/draftjs_exporter-2.1.0.tar.gz
Complete output from command python setup.py egg_info:
Traceback (most recent call last):
File "<string>", line 1, in <module>
File "/tmp/pip-install-rmsz5l7l/draftjs-exporter/setup.py", line 91, in <module>
long_description=md2pypi('README.md'),
File "/tmp/pip-install-rmsz5l7l/draftjs-exporter/setup.py", line 59, in md2pypi
content = io.open(filename).read()
File "/usr/lib/python3.5/encodings/ascii.py", line 26, in decode
return codecs.ascii_decode(input, self.errors)[0]
UnicodeDecodeError: 'ascii' codec can't decode byte 0xe2 in position 1727: ordinal not in range(128)
Any chance for a version update to master that doesn't run the markdown conversion in the setup.py script?
The value for an empty field will change to None
(instead of an empty dictionary). The renderer has to be updated accordingly.
Note: Corresponding changes will need to happen in springload/draftail and springload/wagtaildraftail.
is there a built-in function to do it?
if not, is it possible to set configuration to do it?
thanks!
Could be an announcement blog post for 1.0.0 as well.
I have this block
{
"key": "",
"text": "Bold Italic Underline",
"type": "unstyled",
"depth": 0,
"inlineStyleRanges": [
{"offset": 0, "length": 21, "style": "BOLD"},
{"offset": 5, "length": 16, "style": "ITALIC"},
{"offset": 12, "length": 9, "style": "UNDERLINE"},
],
"entityRanges": [],
"data": {},
},
I expect it to render HTML as
<p><strong>Bold <em>Italic <u>Underline</u></em></strong></p>
but it renders HTML as
<p><strong>Bold </strong><strong><em>Italic </em></strong><strong><em><u>Underline</u></em></strong></p>
Is it a bug or is there any configuration?
To make sure we do not cause any memory leak, and this is ready to be used in production servers.
v0.4 is up on pypi
but the corresponding tag is missing on GitHub :(
My use case need to migrate rich text content into JSON used in DraftJS, is it possible to do this with this code?
This will be corrected by :
with open('README.md', encoding='utf8') as f:
long_description = f.read()
Test version: v2.1.4
Test with the following code:
from draftjs_exporter.constants import BLOCK_TYPES
from draftjs_exporter.constants import ENTITY_TYPES
from draftjs_exporter.defaults import BLOCK_MAP
from draftjs_exporter.dom import DOM
from draftjs_exporter.html import HTML
import json
a = '''{
"blocks": [
{
"key": "bh6r4",
"text": "ππ",
"type": "unstyled",
"depth": 0,
"inlineStyleRanges": [],
"entityRanges": [
{
"offset": 0,
"length": 1,
"key": 7
},
{
"offset": 1,
"length": 1,
"key": 8
}
],
"data": {}
}
],
"entityMap": {
"7": {
"type": "emoji",
"mutability": "IMMUTABLE",
"data": {
"emojiUnicode": "π"
}
},
"8": {
"type": "emoji",
"mutability": "IMMUTABLE",
"data": {
"emojiUnicode": "π"
}
}
}
}'''
def emoji(props):
emoji_encode = []
for c in props.get('emojiUnicode'):
code = '%04x' % ord(c)
if code != '200d':
emoji_encode.append('%04x' % ord(c))
return DOM.create_element('span', {
'data-emoji': '-'.join(emoji_encode),
'class': 'emoji',
}, props['children'])
def entity_fallback(props):
return DOM.create_element('span', {'class': 'missing-entity'},
props['children'])
def style_fallback(props):
return props['children']
def block_fallback(props):
return DOM.create_element('div', {}, props['children'])
DRAFTJS_EXPORTER_CONFIG = {
'entity_decorators': {
'emoji': emoji,
ENTITY_TYPES.FALLBACK: entity_fallback,
},
'block_map': dict(BLOCK_MAP, **{
BLOCK_TYPES.FALLBACK: block_fallback,
})
}
exporter = HTML(DRAFTJS_EXPORTER_CONFIG)
if __name__ == '__main__':
print(exporter.render(json.loads(a)))
Actual output:
<p><span class="emoji" data-emoji="1f644">π</span>π<span class="emoji" data-emoji="1f616"></span></p>
Expected output:
<p><span class="emoji" data-emoji="1f644">π</span><span class="emoji" data-emoji="1f616">π</span></p>
Pointed out by @loicteixeira, this happens a couple of times within the codebase.
Discuss the tricky problems the exporter needs to address. They can be addressed by "doing the right thing", documenting the shortcomings, and/or letting the user configure the output they want.
data-*
attributes but invalid ones like *ngFor
) β ok with html5libem
in strong
or strong
in em
)
from collections import OrderedDict
?
unstyled
blocks without text, should they render as empty p
tags? br
?
draftjs_exporter/draftjs_exporter/dom.py
Line 37 in ce7bff6
Safari in particular will likely complain if namespaces are not handled properly.
The same issue would arise with other namespaces (og, microdata, mathjax, etc)
Hi Thibaud Colas,
Here I got another problem. In my project, when I wrote a real-world note text which is not too long, but with a lot of entities, I found it takes more than 5 seconds to render. Of course that's unacceptable for an online service, so I tried to reduce the number of temporary wrapper elements to optimize the speed, finally I made it a little better, like more than 3 seconds, that's all I could do.
But when I tried to use lxml instead of html5lib, the rendering time decreased to less than 1 second!
WTH? Then I found someone's benchmark , which explains the hug difference (with python 2).
And here's my simple test case with a few images and "subjects". With lxml, the rendering takes 0.17 seconds:
def Soup(raw_str):
"""
Wrapper around BeautifulSoup to keep the code DRY.
"""
return BeautifulSoup(raw_str, 'lxml')
47459 function calls (46117 primitive calls) in 0.164 seconds
Ordered by: cumulative time
List reduced from 257 to 20 due to restriction <20>
ncalls tottime percall cumtime percall filename:lineno(function)
1 0.000 0.000 0.165 0.165 html.py:24(render)
16 0.001 0.000 0.115 0.007 html.py:39(render_block)
133 0.002 0.000 0.091 0.001 dom.py:35(create_element)
38 0.000 0.000 0.091 0.002 entity_state.py:55(render_entitities)
6 0.001 0.000 0.073 0.012 benchmark.py:173(render)
174 0.001 0.000 0.072 0.000 dom.py:17(Soup)
174 0.008 0.000 0.071 0.000 __init__.py:87(__init__)
172 0.001 0.000 0.059 0.000 dom.py:28(create_tag)
1 0.000 0.000 0.049 0.049 wrapper_state.py:111(to_string)
1 0.000 0.000 0.049 0.049 dom.py:124(render)
174 0.001 0.000 0.042 0.000 __init__.py:285(_feed)
174 0.001 0.000 0.041 0.000 _lxml.py:246(feed)
1166/515 0.002 0.000 0.037 0.000 {hasattr}
107 0.001 0.000 0.036 0.000 element.py:1029(__getattr__)
107 0.000 0.000 0.034 0.000 element.py:1273(find)
107 0.001 0.000 0.034 0.000 element.py:1284(find_all)
174 0.007 0.000 0.034 0.000 {method 'feed' of 'lxml.etree._FeedParser' objects}
107 0.003 0.000 0.033 0.000 element.py:518(_find_all)
2 0.000 0.000 0.028 0.014 element.py:1077(__unicode__)
336/2 0.008 0.000 0.028 0.014 element.py:1105(decode)
But with html5lib it takes about 0.65 seconds.
178504 function calls (177142 primitive calls) in 0.663 seconds
Ordered by: cumulative time
List reduced from 455 to 20 due to restriction <20>
ncalls tottime percall cumtime percall filename:lineno(function)
1 0.000 0.000 0.663 0.663 html.py:24(render)
174 0.001 0.000 0.572 0.003 dom.py:17(Soup)
174 0.008 0.000 0.571 0.003 __init__.py:87(__init__)
16 0.001 0.000 0.551 0.034 html.py:39(render_block)
174 0.001 0.000 0.543 0.003 __init__.py:285(_feed)
174 0.003 0.000 0.542 0.003 _html5lib.py:57(feed)
172 0.001 0.000 0.488 0.003 dom.py:28(create_tag)
133 0.002 0.000 0.412 0.003 dom.py:35(create_element)
38 0.000 0.000 0.382 0.010 entity_state.py:55(render_entitities)
174 0.011 0.000 0.350 0.002 html5parser.py:55(__init__)
6 0.001 0.000 0.256 0.043 benchmark.py:173(render)
174 0.001 0.000 0.188 0.001 html5parser.py:225(parse)
174 0.002 0.000 0.187 0.001 html5parser.py:81(_parse)
5916 0.104 0.000 0.176 0.000 utils.py:49(__init__)
174 0.007 0.000 0.145 0.001 html5parser.py:157(mainLoop)
1 0.000 0.000 0.104 0.104 wrapper_state.py:111(to_string)
1 0.000 0.000 0.104 0.104 dom.py:124(render)
174 0.019 0.000 0.090 0.001 html5parser.py:874(__init__)
16 0.000 0.000 0.089 0.006 wrapper_state.py:125(element_for)
174 0.054 0.000 0.086 0.000 html5parser.py:422(getPhases)
So, any suggestion for optimizing? And I don't know if html5lib is good enough for us to ignore the performance issue, how do you think? Thank you~
Right now custom entity renderes like Link
do not define where their children go β the text or further elements just get appended at the end.
This is a major problem for entities that render text as a prefix or suffix.
The exporter should support blocks going from depth 0 to depth 3, or any depth jump, as this frequently happens with real-world HTML.
Ubuntu 16.04
python 3.5
Collecting draftjs_exporter (from -r requirements.txt (line 4))
Downloading https://files.pythonhosted.org/packages/c3/98/2ae0db16e3841d9d0623b1a2248987e1edd037ab7eaa04e45e4fdf18873b/draftjs_exporter-2.1.1.tar.gz
Complete output from command python setup.py egg_info:
Traceback (most recent call last):
File "<string>", line 1, in <module>
File "/tmp/pip-build-f_emtego/draftjs-exporter/setup.py", line 38, in <module>
long_description = f.read()
File "/usr/lib/python3.5/encodings/ascii.py", line 26, in decode
return codecs.ascii_decode(input, self.errors)[0]
UnicodeDecodeError: 'ascii' codec can't decode byte 0xe2 in position 1956: ordinal not in range(128)
Capturing offline discussions for a public discussion.
Currently a decorator can be written as a function
(accepting a single positional argument props
) or a class
(with a single render
method accepting a single positional argument props
) which has a few issues:
__init__
of the class
but instead everything is passed to props
which makes the class
quite useless. Moving the props to the __init__
will either create boilerplate or will force the user to inherit from a custom class which does that for them. In short, it's not clear which benefit there is to use a class.We were therefore thinking of removing support for class based decorators. Any thoughts?
I have two custom components: image and direct speech. There's nothing very fancy in their code, e.g. image is rendered like this:
class Image:
def render(self, props):
data = props['block']['data']
return self.wrapper(data)
def wrapper(self, data):
caption = data.get('caption', '')
return tags.div(
{'class': 'contentImage'},
self.image(data),
DOM.create_text_node(caption))
def image(self, data):
src = data.get('image', '')
caption = data.get('caption', '')
return tags.img({
'src': src,
'class': 'contentImage-image',
'alt': caption})
then I generate the content for the blocks (not important fields are dropped):
{
"blocks":[
{
"text":"Start text",
"type":"unstyled",
...
},
{
"type":"image",
"data":{
"caption":"Some image caption",
"image":"http://placekitten.com/401/402"
},
...
},
{
"text":"Middle text",
"type":"unstyled",
...
},
{
"type":"direct-speech",
"data":{
"name":"Steve Jobs",
"image":"http://placekitten.com/500/501",
"text":"Hello, world!!!!111"
},
...
},
{
"text":"Last text",
"type":"unstyled",
...
}
]
...
}
I expect "Middle text" block to be between the image and direct speech, but for some reason these two blocks are rendered together and the text appears before them:
<p>Start text</p>
<p>Middle text</p>
<div>
<div class="contentImage">
<img class="contentImage-image" src="http://placekitten.com/401/402">Some image caption
</div>
<blockquote class="directSpeech">
<div class="directSpeech-image" style="background-image: url(http://placekitten.com/500/501);"></div>
<div class="directSpeech-wrapper">
<div class="directSpeech-title">
Steve Jobs
</div>
<p class="directSpeech-text">Hello, world!!!!111</p>
</div>
</blockquote>
</div>
<p>Last text</p>
I hope the bug is reproducable; if you have any questions or need more details I'll provide you any information.
Forgive my naivete, but why is this package needed? What is its purpose / use case for Python devs? I just found out about draft.js, and don't know a lot about javascript. Thanks.
The exporter now has an engine that doesn't have any dependencies. It should probably be the one activated by default, to make the whole package dependency-free unless another engine is configured. It also happens to be faster, and less memory-hungry.
This is a breaking change though, no matter how little difference there is with the output of the current default (html5lib + BeautifulSoup), so should be part of the 2.0.0 release.
As part of this change, it will also be necessary to move the html5lib / BS4 dependencies to a separate extra like for lxml (pip install draftjs_exporter[html5lib]
), as well as update the documentation.
Before upgrading, we used unstyled
as a empty string ''
After 0.8(if I'm not mistaken) draftjs_exporter
will create a empty text instead of original text content
A declarative, efficient, and flexible JavaScript library for building user interfaces.
π Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. πππ
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google β€οΈ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.