##// END OF EJS Templates
setup: support Bleach 4.2 for Python 3.10 support...
Mads Kiilerich -
r8699:7a4e2c6e stable
parent child Browse files
Show More
@@ -1,242 +1,242 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 # This program is free software: you can redistribute it and/or modify
2 # This program is free software: you can redistribute it and/or modify
3 # it under the terms of the GNU General Public License as published by
3 # it under the terms of the GNU General Public License as published by
4 # the Free Software Foundation, either version 3 of the License, or
4 # the Free Software Foundation, either version 3 of the License, or
5 # (at your option) any later version.
5 # (at your option) any later version.
6 #
6 #
7 # This program is distributed in the hope that it will be useful,
7 # This program is distributed in the hope that it will be useful,
8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 # GNU General Public License for more details.
10 # GNU General Public License for more details.
11 #
11 #
12 # You should have received a copy of the GNU General Public License
12 # You should have received a copy of the GNU General Public License
13 # along with this program. If not, see <http://www.gnu.org/licenses/>.
13 # along with this program. If not, see <http://www.gnu.org/licenses/>.
14 """
14 """
15 kallithea.lib.markup_renderer
15 kallithea.lib.markup_renderer
16 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
16 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
17
17
18 Renderer for markup languages with ability to parse using rst or markdown
18 Renderer for markup languages with ability to parse using rst or markdown
19
19
20 This file was forked by the Kallithea project in July 2014.
20 This file was forked by the Kallithea project in July 2014.
21 Original author and date, and relevant copyright and licensing information is below:
21 Original author and date, and relevant copyright and licensing information is below:
22 :created_on: Oct 27, 2011
22 :created_on: Oct 27, 2011
23 :author: marcink
23 :author: marcink
24 :copyright: (c) 2013 RhodeCode GmbH, and others.
24 :copyright: (c) 2013 RhodeCode GmbH, and others.
25 :license: GPLv3, see LICENSE.md for more details.
25 :license: GPLv3, see LICENSE.md for more details.
26 """
26 """
27
27
28
28
29 import hashlib
29 import hashlib
30 import logging
30 import logging
31 import re
31 import re
32 import traceback
32 import traceback
33
33
34 import bleach
34 import bleach
35 import markdown as markdown_mod
35 import markdown as markdown_mod
36 from docutils.core import publish_parts
36 from docutils.core import publish_parts
37 from docutils.parsers.rst import directives
37 from docutils.parsers.rst import directives
38
38
39 from kallithea.lib import webutils
39 from kallithea.lib import webutils
40
40
41
41
42 log = logging.getLogger(__name__)
42 log = logging.getLogger(__name__)
43
43
44
44
45 class MarkupRenderer(object):
45 class MarkupRenderer(object):
46 RESTRUCTUREDTEXT_DISALLOWED_DIRECTIVES = ['include', 'meta', 'raw']
46 RESTRUCTUREDTEXT_DISALLOWED_DIRECTIVES = ['include', 'meta', 'raw']
47
47
48 MARKDOWN_PAT = re.compile(r'md|mkdn?|mdown|markdown', re.IGNORECASE)
48 MARKDOWN_PAT = re.compile(r'md|mkdn?|mdown|markdown', re.IGNORECASE)
49 RST_PAT = re.compile(r're?st', re.IGNORECASE)
49 RST_PAT = re.compile(r're?st', re.IGNORECASE)
50 PLAIN_PAT = re.compile(r'readme', re.IGNORECASE)
50 PLAIN_PAT = re.compile(r'readme', re.IGNORECASE)
51
51
52 @classmethod
52 @classmethod
53 def _detect_renderer(cls, source, filename):
53 def _detect_renderer(cls, source, filename):
54 """
54 """
55 runs detection of what renderer should be used for generating html
55 runs detection of what renderer should be used for generating html
56 from a markup language
56 from a markup language
57
57
58 filename can be also explicitly a renderer name
58 filename can be also explicitly a renderer name
59 """
59 """
60
60
61 if cls.MARKDOWN_PAT.findall(filename):
61 if cls.MARKDOWN_PAT.findall(filename):
62 return cls.markdown
62 return cls.markdown
63 elif cls.RST_PAT.findall(filename):
63 elif cls.RST_PAT.findall(filename):
64 return cls.rst
64 return cls.rst
65 elif cls.PLAIN_PAT.findall(filename):
65 elif cls.PLAIN_PAT.findall(filename):
66 return cls.rst
66 return cls.rst
67 return cls.plain
67 return cls.plain
68
68
69 @classmethod
69 @classmethod
70 def _flavored_markdown(cls, text):
70 def _flavored_markdown(cls, text):
71 """
71 """
72 Github style flavored markdown
72 Github style flavored markdown
73
73
74 :param text:
74 :param text:
75 """
75 """
76
76
77 # Extract pre blocks.
77 # Extract pre blocks.
78 extractions = {}
78 extractions = {}
79
79
80 def pre_extraction_callback(matchobj):
80 def pre_extraction_callback(matchobj):
81 digest = hashlib.sha1(matchobj.group(0)).hexdigest()
81 digest = hashlib.sha1(matchobj.group(0)).hexdigest()
82 extractions[digest] = matchobj.group(0)
82 extractions[digest] = matchobj.group(0)
83 return "{gfm-extraction-%s}" % digest
83 return "{gfm-extraction-%s}" % digest
84 pattern = re.compile(r'<pre>.*?</pre>', re.MULTILINE | re.DOTALL)
84 pattern = re.compile(r'<pre>.*?</pre>', re.MULTILINE | re.DOTALL)
85 text = re.sub(pattern, pre_extraction_callback, text)
85 text = re.sub(pattern, pre_extraction_callback, text)
86
86
87 # Prevent foo_bar_baz from ending up with an italic word in the middle.
87 # Prevent foo_bar_baz from ending up with an italic word in the middle.
88 def italic_callback(matchobj):
88 def italic_callback(matchobj):
89 s = matchobj.group(0)
89 s = matchobj.group(0)
90 if list(s).count('_') >= 2:
90 if list(s).count('_') >= 2:
91 return s.replace('_', r'\_')
91 return s.replace('_', r'\_')
92 return s
92 return s
93 text = re.sub(r'^(?! {4}|\t)\w+_\w+_\w[\w_]*', italic_callback, text)
93 text = re.sub(r'^(?! {4}|\t)\w+_\w+_\w[\w_]*', italic_callback, text)
94
94
95 # In very clear cases, let newlines become <br /> tags.
95 # In very clear cases, let newlines become <br /> tags.
96 def newline_callback(matchobj):
96 def newline_callback(matchobj):
97 if len(matchobj.group(1)) == 1:
97 if len(matchobj.group(1)) == 1:
98 return matchobj.group(0).rstrip() + ' \n'
98 return matchobj.group(0).rstrip() + ' \n'
99 else:
99 else:
100 return matchobj.group(0)
100 return matchobj.group(0)
101 pattern = re.compile(r'^[\w\<][^\n]*(\n+)', re.MULTILINE)
101 pattern = re.compile(r'^[\w\<][^\n]*(\n+)', re.MULTILINE)
102 text = re.sub(pattern, newline_callback, text)
102 text = re.sub(pattern, newline_callback, text)
103
103
104 # Insert pre block extractions.
104 # Insert pre block extractions.
105 def pre_insert_callback(matchobj):
105 def pre_insert_callback(matchobj):
106 return '\n\n' + extractions[matchobj.group(1)]
106 return '\n\n' + extractions[matchobj.group(1)]
107 text = re.sub(r'{gfm-extraction-([0-9a-f]{32})\}',
107 text = re.sub(r'{gfm-extraction-([0-9a-f]{32})\}',
108 pre_insert_callback, text)
108 pre_insert_callback, text)
109
109
110 return text
110 return text
111
111
112 @classmethod
112 @classmethod
113 def render(cls, source, filename=None):
113 def render(cls, source, filename=None):
114 """
114 """
115 Renders a given filename using detected renderer
115 Renders a given filename using detected renderer
116 it detects renderers based on file extension or mimetype.
116 it detects renderers based on file extension or mimetype.
117 At last it will just do a simple html replacing new lines with <br/>
117 At last it will just do a simple html replacing new lines with <br/>
118
118
119 >>> MarkupRenderer.render('''<img id="a" style="margin-top:-1000px;color:red" src="http://example.com/test.jpg">''', '.md')
119 >>> MarkupRenderer.render('''<img id="a" style="margin-top:-1000px;color:red" src="http://example.com/test.jpg">''', '.md')
120 '<p><img id="a" src="http://example.com/test.jpg" style="color: red;"></p>'
120 '<p><img id="a" src="http://example.com/test.jpg" style=""></p>'
121 >>> MarkupRenderer.render('''<img class="c d" src="file://localhost/test.jpg">''', 'b.mkd')
121 >>> MarkupRenderer.render('''<img class="c d" src="file://localhost/test.jpg">''', 'b.mkd')
122 '<p><img class="c d"></p>'
122 '<p><img class="c d"></p>'
123 >>> MarkupRenderer.render('''<a href="foo">foo</a>''', 'c.mkdn')
123 >>> MarkupRenderer.render('''<a href="foo">foo</a>''', 'c.mkdn')
124 '<p><a href="foo">foo</a></p>'
124 '<p><a href="foo">foo</a></p>'
125 >>> MarkupRenderer.render('''<script>alert(1)</script>''', 'd.mdown')
125 >>> MarkupRenderer.render('''<script>alert(1)</script>''', 'd.mdown')
126 '&lt;script&gt;alert(1)&lt;/script&gt;'
126 '&lt;script&gt;alert(1)&lt;/script&gt;'
127 >>> MarkupRenderer.render('''<div onclick="alert(2)">yo</div>''', 'markdown')
127 >>> MarkupRenderer.render('''<div onclick="alert(2)">yo</div>''', 'markdown')
128 '<div>yo</div>'
128 '<div>yo</div>'
129 >>> MarkupRenderer.render('''<a href="javascript:alert(3)">yo</a>''', 'md')
129 >>> MarkupRenderer.render('''<a href="javascript:alert(3)">yo</a>''', 'md')
130 '<p><a>yo</a></p>'
130 '<p><a>yo</a></p>'
131 """
131 """
132
132
133 renderer = cls._detect_renderer(source, filename)
133 renderer = cls._detect_renderer(source, filename)
134 readme_data = renderer(source)
134 readme_data = renderer(source)
135 # Allow most HTML, while preventing XSS issues:
135 # Allow most HTML, while preventing XSS issues:
136 # no <script> tags, no onclick attributes, no javascript
136 # no <script> tags, no onclick attributes, no javascript
137 # "protocol", and also limit styling to prevent defacing.
137 # "protocol", and also limit styling to prevent defacing.
138 return bleach.clean(readme_data,
138 return bleach.clean(readme_data,
139 tags=['a', 'abbr', 'b', 'blockquote', 'br', 'code', 'dd',
139 tags=['a', 'abbr', 'b', 'blockquote', 'br', 'code', 'dd',
140 'div', 'dl', 'dt', 'em', 'h1', 'h2', 'h3', 'h4', 'h5',
140 'div', 'dl', 'dt', 'em', 'h1', 'h2', 'h3', 'h4', 'h5',
141 'h6', 'hr', 'i', 'img', 'li', 'ol', 'p', 'pre', 'span',
141 'h6', 'hr', 'i', 'img', 'li', 'ol', 'p', 'pre', 'span',
142 'strong', 'sub', 'sup', 'table', 'tbody', 'td', 'th',
142 'strong', 'sub', 'sup', 'table', 'tbody', 'td', 'th',
143 'thead', 'tr', 'ul'],
143 'thead', 'tr', 'ul'],
144 attributes=['class', 'id', 'style', 'label', 'title', 'alt', 'href', 'src'],
144 attributes=['class', 'id', 'style', 'label', 'title', 'alt', 'href', 'src'],
145 styles=['color'],
145 styles=['color'],
146 protocols=['http', 'https', 'mailto'],
146 protocols=['http', 'https', 'mailto'],
147 )
147 )
148
148
149 @classmethod
149 @classmethod
150 def plain(cls, source, universal_newline=True):
150 def plain(cls, source, universal_newline=True):
151 """
151 """
152 >>> MarkupRenderer.plain('https://example.com/')
152 >>> MarkupRenderer.plain('https://example.com/')
153 '<br /><a href="https://example.com/">https://example.com/</a>'
153 '<br /><a href="https://example.com/">https://example.com/</a>'
154 """
154 """
155 if universal_newline:
155 if universal_newline:
156 newline = '\n'
156 newline = '\n'
157 source = newline.join(source.splitlines())
157 source = newline.join(source.splitlines())
158
158
159 def url_func(match_obj):
159 def url_func(match_obj):
160 url_full = match_obj.group(0)
160 url_full = match_obj.group(0)
161 return '<a href="%(url)s">%(url)s</a>' % ({'url': url_full})
161 return '<a href="%(url)s">%(url)s</a>' % ({'url': url_full})
162 source = webutils.url_re.sub(url_func, source)
162 source = webutils.url_re.sub(url_func, source)
163 return '<br />' + source.replace("\n", '<br />')
163 return '<br />' + source.replace("\n", '<br />')
164
164
165 @classmethod
165 @classmethod
166 def markdown(cls, source, safe=True, flavored=False):
166 def markdown(cls, source, safe=True, flavored=False):
167 """
167 """
168 Convert Markdown (possibly GitHub Flavored) to INSECURE HTML, possibly
168 Convert Markdown (possibly GitHub Flavored) to INSECURE HTML, possibly
169 with "safe" fall-back to plaintext. Output from this method should be sanitized before use.
169 with "safe" fall-back to plaintext. Output from this method should be sanitized before use.
170
170
171 >>> MarkupRenderer.markdown('''<img id="a" style="margin-top:-1000px;color:red" src="http://example.com/test.jpg">''')
171 >>> MarkupRenderer.markdown('''<img id="a" style="margin-top:-1000px;color:red" src="http://example.com/test.jpg">''')
172 '<p><img id="a" style="margin-top:-1000px;color:red" src="http://example.com/test.jpg"></p>'
172 '<p><img id="a" style="margin-top:-1000px;color:red" src="http://example.com/test.jpg"></p>'
173 >>> MarkupRenderer.markdown('''<img class="c d" src="file://localhost/test.jpg">''')
173 >>> MarkupRenderer.markdown('''<img class="c d" src="file://localhost/test.jpg">''')
174 '<p><img class="c d" src="file://localhost/test.jpg"></p>'
174 '<p><img class="c d" src="file://localhost/test.jpg"></p>'
175 >>> MarkupRenderer.markdown('''<a href="foo">foo</a>''')
175 >>> MarkupRenderer.markdown('''<a href="foo">foo</a>''')
176 '<p><a href="foo">foo</a></p>'
176 '<p><a href="foo">foo</a></p>'
177 >>> MarkupRenderer.markdown('''<script>alert(1)</script>''')
177 >>> MarkupRenderer.markdown('''<script>alert(1)</script>''')
178 '<script>alert(1)</script>'
178 '<script>alert(1)</script>'
179 >>> MarkupRenderer.markdown('''<div onclick="alert(2)">yo</div>''')
179 >>> MarkupRenderer.markdown('''<div onclick="alert(2)">yo</div>''')
180 '<div onclick="alert(2)">yo</div>'
180 '<div onclick="alert(2)">yo</div>'
181 >>> MarkupRenderer.markdown('''<a href="javascript:alert(3)">yo</a>''')
181 >>> MarkupRenderer.markdown('''<a href="javascript:alert(3)">yo</a>''')
182 '<p><a href="javascript:alert(3)">yo</a></p>'
182 '<p><a href="javascript:alert(3)">yo</a></p>'
183 >>> MarkupRenderer.markdown('''## Foo''')
183 >>> MarkupRenderer.markdown('''## Foo''')
184 '<h2>Foo</h2>'
184 '<h2>Foo</h2>'
185 >>> print(MarkupRenderer.markdown('''
185 >>> print(MarkupRenderer.markdown('''
186 ... #!/bin/bash
186 ... #!/bin/bash
187 ... echo "hello"
187 ... echo "hello"
188 ... '''))
188 ... '''))
189 <table class="code-highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre>1
189 <table class="code-highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre>1
190 2</pre></div></td><td class="code"><div class="code-highlight"><pre><span></span><span class="ch">#!/bin/bash</span>
190 2</pre></div></td><td class="code"><div class="code-highlight"><pre><span></span><span class="ch">#!/bin/bash</span>
191 <span class="nb">echo</span> <span class="s2">&quot;hello&quot;</span>
191 <span class="nb">echo</span> <span class="s2">&quot;hello&quot;</span>
192 </pre></div>
192 </pre></div>
193 </td></tr></table>
193 </td></tr></table>
194 """
194 """
195 try:
195 try:
196 if flavored:
196 if flavored:
197 source = cls._flavored_markdown(source)
197 source = cls._flavored_markdown(source)
198 return markdown_mod.markdown(
198 return markdown_mod.markdown(
199 source,
199 source,
200 extensions=['markdown.extensions.codehilite', 'markdown.extensions.extra'],
200 extensions=['markdown.extensions.codehilite', 'markdown.extensions.extra'],
201 extension_configs={'markdown.extensions.codehilite': {'css_class': 'code-highlight'}})
201 extension_configs={'markdown.extensions.codehilite': {'css_class': 'code-highlight'}})
202 except Exception:
202 except Exception:
203 log.error(traceback.format_exc())
203 log.error(traceback.format_exc())
204 if safe:
204 if safe:
205 log.debug('Falling back to render in plain mode')
205 log.debug('Falling back to render in plain mode')
206 return cls.plain(source)
206 return cls.plain(source)
207 else:
207 else:
208 raise
208 raise
209
209
210 @classmethod
210 @classmethod
211 def rst(cls, source, safe=True):
211 def rst(cls, source, safe=True):
212 try:
212 try:
213 docutils_settings = dict([(alias, None) for alias in
213 docutils_settings = dict([(alias, None) for alias in
214 cls.RESTRUCTUREDTEXT_DISALLOWED_DIRECTIVES])
214 cls.RESTRUCTUREDTEXT_DISALLOWED_DIRECTIVES])
215
215
216 docutils_settings.update({'input_encoding': 'unicode',
216 docutils_settings.update({'input_encoding': 'unicode',
217 'report_level': 4})
217 'report_level': 4})
218
218
219 for k, v in docutils_settings.items():
219 for k, v in docutils_settings.items():
220 directives.register_directive(k, v)
220 directives.register_directive(k, v)
221
221
222 parts = publish_parts(source=source,
222 parts = publish_parts(source=source,
223 writer_name="html4css1",
223 writer_name="html4css1",
224 settings_overrides=docutils_settings)
224 settings_overrides=docutils_settings)
225
225
226 return parts['html_title'] + parts["fragment"]
226 return parts['html_title'] + parts["fragment"]
227 except Exception:
227 except Exception:
228 log.error(traceback.format_exc())
228 log.error(traceback.format_exc())
229 if safe:
229 if safe:
230 log.debug('Falling back to render in plain mode')
230 log.debug('Falling back to render in plain mode')
231 return cls.plain(source)
231 return cls.plain(source)
232 else:
232 else:
233 raise
233 raise
234
234
235 @classmethod
235 @classmethod
236 def rst_with_mentions(cls, source):
236 def rst_with_mentions(cls, source):
237
237
238 def wrapp(match_obj):
238 def wrapp(match_obj):
239 uname = match_obj.groups()[0]
239 uname = match_obj.groups()[0]
240 return r'\ **@%(uname)s**\ ' % {'uname': uname}
240 return r'\ **@%(uname)s**\ ' % {'uname': uname}
241 mention_hl = webutils.MENTIONS_REGEX.sub(wrapp, source).strip()
241 mention_hl = webutils.MENTIONS_REGEX.sub(wrapp, source).strip()
242 return cls.rst(mention_hl)
242 return cls.rst(mention_hl)
@@ -1,162 +1,162 b''
1 #!/usr/bin/env python3
1 #!/usr/bin/env python3
2 # -*- coding: utf-8 -*-
2 # -*- coding: utf-8 -*-
3 import os
3 import os
4 import platform
4 import platform
5 import re
5 import re
6 import sys
6 import sys
7
7
8 import setuptools
8 import setuptools
9 # monkey patch setuptools to use distutils owner/group functionality
9 # monkey patch setuptools to use distutils owner/group functionality
10 from setuptools.command import sdist
10 from setuptools.command import sdist
11
11
12
12
13 if sys.version_info < (3, 6):
13 if sys.version_info < (3, 6):
14 raise Exception('Kallithea requires Python 3.6 or later')
14 raise Exception('Kallithea requires Python 3.6 or later')
15
15
16
16
17 here = os.path.abspath(os.path.dirname(__file__))
17 here = os.path.abspath(os.path.dirname(__file__))
18
18
19
19
20 def _get_meta_var(name, data, callback_handler=None):
20 def _get_meta_var(name, data, callback_handler=None):
21 matches = re.compile(r'(?:%s)\s*=\s*(.*)' % name).search(data)
21 matches = re.compile(r'(?:%s)\s*=\s*(.*)' % name).search(data)
22 if matches:
22 if matches:
23 s = eval(matches.groups()[0])
23 s = eval(matches.groups()[0])
24 if callable(callback_handler):
24 if callable(callback_handler):
25 return callback_handler(s)
25 return callback_handler(s)
26 return s
26 return s
27
27
28 _meta = open(os.path.join(here, 'kallithea', '__init__.py'), 'r')
28 _meta = open(os.path.join(here, 'kallithea', '__init__.py'), 'r')
29 _metadata = _meta.read()
29 _metadata = _meta.read()
30 _meta.close()
30 _meta.close()
31
31
32 def callback(V):
32 def callback(V):
33 return '.'.join(map(str, V[:3])) + '.'.join(V[3:])
33 return '.'.join(map(str, V[:3])) + '.'.join(V[3:])
34 __version__ = _get_meta_var('VERSION', _metadata, callback)
34 __version__ = _get_meta_var('VERSION', _metadata, callback)
35 __license__ = _get_meta_var('__license__', _metadata)
35 __license__ = _get_meta_var('__license__', _metadata)
36 __author__ = _get_meta_var('__author__', _metadata)
36 __author__ = _get_meta_var('__author__', _metadata)
37 __url__ = _get_meta_var('__url__', _metadata)
37 __url__ = _get_meta_var('__url__', _metadata)
38 # defines current platform
38 # defines current platform
39 __platform__ = platform.system()
39 __platform__ = platform.system()
40
40
41 is_windows = __platform__ in ['Windows']
41 is_windows = __platform__ in ['Windows']
42
42
43 requirements = [
43 requirements = [
44 "alembic >= 1.0.10, < 1.5",
44 "alembic >= 1.0.10, < 1.5",
45 "gearbox >= 0.1.0, < 1",
45 "gearbox >= 0.1.0, < 1",
46 "waitress >= 0.8.8, < 1.5",
46 "waitress >= 0.8.8, < 1.5",
47 "WebOb >= 1.8, < 1.9",
47 "WebOb >= 1.8, < 1.9",
48 "backlash >= 0.1.2, < 1",
48 "backlash >= 0.1.2, < 1",
49 "TurboGears2 >= 2.4, < 2.5",
49 "TurboGears2 >= 2.4, < 2.5",
50 "tgext.routes >= 0.2.0, < 1",
50 "tgext.routes >= 0.2.0, < 1",
51 "Beaker >= 1.10.1, < 2",
51 "Beaker >= 1.10.1, < 2",
52 "WebHelpers2 >= 2.0, < 2.1",
52 "WebHelpers2 >= 2.0, < 2.1",
53 "FormEncode >= 1.3.1, < 2.1",
53 "FormEncode >= 1.3.1, < 2.1",
54 "SQLAlchemy >= 1.2.9, < 1.4",
54 "SQLAlchemy >= 1.2.9, < 1.4",
55 "Mako >= 0.9.1, < 1.2",
55 "Mako >= 0.9.1, < 1.2",
56 "Pygments >= 2.2.0, < 2.7",
56 "Pygments >= 2.2.0, < 2.7",
57 "Whoosh >= 2.7.1, < 2.8",
57 "Whoosh >= 2.7.1, < 2.8",
58 "celery >= 5, < 5.1",
58 "celery >= 5, < 5.1",
59 "Babel >= 1.3, < 2.9",
59 "Babel >= 1.3, < 2.9",
60 "python-dateutil >= 2.1.0, < 2.9",
60 "python-dateutil >= 2.1.0, < 2.9",
61 "Markdown >= 2.2.1, < 3.2",
61 "Markdown >= 2.2.1, < 3.2",
62 "docutils >= 0.11, < 0.17",
62 "docutils >= 0.11, < 0.17",
63 "URLObject >= 2.3.4, < 2.5",
63 "URLObject >= 2.3.4, < 2.5",
64 "Routes >= 2.0, < 2.5",
64 "Routes >= 2.0, < 2.5",
65 "dulwich >= 0.19.0, < 0.20",
65 "dulwich >= 0.19.0, < 0.20",
66 "mercurial >= 5.2, < 5.9",
66 "mercurial >= 5.2, < 5.9",
67 "decorator >= 4.2.1, < 4.5",
67 "decorator >= 4.2.1, < 4.5",
68 "Paste >= 2.0.3, < 3.5",
68 "Paste >= 2.0.3, < 3.5",
69 "bleach >= 3.0, < 3.1.4",
69 "bleach >= 3.2, < 4.2",
70 "Click >= 7.0, < 8",
70 "Click >= 7.0, < 8",
71 "ipaddr >= 2.2.0, < 2.3",
71 "ipaddr >= 2.2.0, < 2.3",
72 "paginate >= 0.5, < 0.6",
72 "paginate >= 0.5, < 0.6",
73 "paginate_sqlalchemy >= 0.3.0, < 0.4",
73 "paginate_sqlalchemy >= 0.3.0, < 0.4",
74 "bcrypt >= 3.1.0, < 3.2",
74 "bcrypt >= 3.1.0, < 3.2",
75 "pip >= 20.0, < 999",
75 "pip >= 20.0, < 999",
76 "chardet >= 3",
76 "chardet >= 3",
77 ]
77 ]
78
78
79 dependency_links = [
79 dependency_links = [
80 ]
80 ]
81
81
82 classifiers = [
82 classifiers = [
83 'Development Status :: 4 - Beta',
83 'Development Status :: 4 - Beta',
84 'Environment :: Web Environment',
84 'Environment :: Web Environment',
85 'Framework :: Pylons',
85 'Framework :: Pylons',
86 'Intended Audience :: Developers',
86 'Intended Audience :: Developers',
87 'License :: OSI Approved :: GNU General Public License (GPL)',
87 'License :: OSI Approved :: GNU General Public License (GPL)',
88 'Operating System :: OS Independent',
88 'Operating System :: OS Independent',
89 'Programming Language :: Python :: 3.6',
89 'Programming Language :: Python :: 3.6',
90 'Programming Language :: Python :: 3.7',
90 'Programming Language :: Python :: 3.7',
91 'Programming Language :: Python :: 3.8',
91 'Programming Language :: Python :: 3.8',
92 'Topic :: Software Development :: Version Control',
92 'Topic :: Software Development :: Version Control',
93 ]
93 ]
94
94
95
95
96 # additional files from project that goes somewhere in the filesystem
96 # additional files from project that goes somewhere in the filesystem
97 # relative to sys.prefix
97 # relative to sys.prefix
98 data_files = []
98 data_files = []
99
99
100 description = ('Kallithea is a fast and powerful management tool '
100 description = ('Kallithea is a fast and powerful management tool '
101 'for Mercurial and Git with a built in push/pull server, '
101 'for Mercurial and Git with a built in push/pull server, '
102 'full text search and code-review.')
102 'full text search and code-review.')
103
103
104 keywords = ' '.join([
104 keywords = ' '.join([
105 'kallithea', 'mercurial', 'git', 'code review',
105 'kallithea', 'mercurial', 'git', 'code review',
106 'repo groups', 'ldap', 'repository management', 'hgweb replacement',
106 'repo groups', 'ldap', 'repository management', 'hgweb replacement',
107 'hgwebdir', 'gitweb replacement', 'serving hgweb',
107 'hgwebdir', 'gitweb replacement', 'serving hgweb',
108 ])
108 ])
109
109
110 # long description
110 # long description
111 README_FILE = 'README.rst'
111 README_FILE = 'README.rst'
112 try:
112 try:
113 long_description = open(README_FILE).read()
113 long_description = open(README_FILE).read()
114 except IOError as err:
114 except IOError as err:
115 sys.stderr.write(
115 sys.stderr.write(
116 "[WARNING] Cannot find file specified as long_description (%s): %s\n"
116 "[WARNING] Cannot find file specified as long_description (%s): %s\n"
117 % (README_FILE, err)
117 % (README_FILE, err)
118 )
118 )
119 long_description = description
119 long_description = description
120
120
121
121
122 sdist_org = sdist.sdist
122 sdist_org = sdist.sdist
123 class sdist_new(sdist_org):
123 class sdist_new(sdist_org):
124 def initialize_options(self):
124 def initialize_options(self):
125 sdist_org.initialize_options(self)
125 sdist_org.initialize_options(self)
126 self.owner = self.group = 'root'
126 self.owner = self.group = 'root'
127 sdist.sdist = sdist_new
127 sdist.sdist = sdist_new
128
128
129 packages = setuptools.find_packages(exclude=['ez_setup'])
129 packages = setuptools.find_packages(exclude=['ez_setup'])
130
130
131 setuptools.setup(
131 setuptools.setup(
132 name='Kallithea',
132 name='Kallithea',
133 version=__version__,
133 version=__version__,
134 description=description,
134 description=description,
135 long_description=long_description,
135 long_description=long_description,
136 keywords=keywords,
136 keywords=keywords,
137 license=__license__,
137 license=__license__,
138 author=__author__,
138 author=__author__,
139 author_email='kallithea@sfconservancy.org',
139 author_email='kallithea@sfconservancy.org',
140 dependency_links=dependency_links,
140 dependency_links=dependency_links,
141 url=__url__,
141 url=__url__,
142 install_requires=requirements,
142 install_requires=requirements,
143 classifiers=classifiers,
143 classifiers=classifiers,
144 data_files=data_files,
144 data_files=data_files,
145 packages=packages,
145 packages=packages,
146 include_package_data=True,
146 include_package_data=True,
147 message_extractors={'kallithea': [
147 message_extractors={'kallithea': [
148 ('**.py', 'python', None),
148 ('**.py', 'python', None),
149 ('templates/**.mako', 'mako', {'input_encoding': 'utf-8'}),
149 ('templates/**.mako', 'mako', {'input_encoding': 'utf-8'}),
150 ('templates/**.html', 'mako', {'input_encoding': 'utf-8'}),
150 ('templates/**.html', 'mako', {'input_encoding': 'utf-8'}),
151 ('public/**', 'ignore', None)]},
151 ('public/**', 'ignore', None)]},
152 zip_safe=False,
152 zip_safe=False,
153 entry_points="""
153 entry_points="""
154 [console_scripts]
154 [console_scripts]
155 kallithea-api = kallithea.bin.kallithea_api:main
155 kallithea-api = kallithea.bin.kallithea_api:main
156 kallithea-gist = kallithea.bin.kallithea_gist:main
156 kallithea-gist = kallithea.bin.kallithea_gist:main
157 kallithea-cli = kallithea.bin.kallithea_cli:cli
157 kallithea-cli = kallithea.bin.kallithea_cli:cli
158
158
159 [paste.app_factory]
159 [paste.app_factory]
160 main = kallithea.config.application:make_app
160 main = kallithea.config.application:make_app
161 """,
161 """,
162 )
162 )
General Comments 0
You need to be logged in to leave comments. Login now