py3: Switch almost all shebang lines to use `python3`.
This causes `upgrade-zulip-from-git`, as well as a no-option run of
`tools/build-release-tarball`, to produce a Zulip install running
Python 3, rather than Python 2. In particular this means that the
virtualenv we create, in which all application code runs, is Python 3.
One shebang line, on `zulip-ec2-configure-interfaces`, explicitly
keeps Python 2, and at least one external ops script, `wal-e`, also
still runs on Python 2. See discussion on the respective previous
commits that made those explicit. There may also be some other
third-party scripts we use, outside of this source tree and running
outside our virtualenv, that still run on Python 2.
2017-08-02 23:15:16 +02:00
|
|
|
#!/usr/bin/env python3
|
2016-11-24 19:45:25 +01:00
|
|
|
from __future__ import print_function
|
2017-02-05 21:24:28 +01:00
|
|
|
from __future__ import absolute_import
|
|
|
|
|
|
|
|
# check for the venv
|
|
|
|
from lib import sanity_check
|
|
|
|
sanity_check.check_venv(__file__)
|
2016-11-24 19:45:25 +01:00
|
|
|
|
|
|
|
from collections import defaultdict
|
2017-03-03 19:01:52 +01:00
|
|
|
from typing import Any, Dict, Iterable, List, Set
|
2016-11-24 19:45:25 +01:00
|
|
|
|
|
|
|
import cgi
|
|
|
|
import os
|
|
|
|
import pprint
|
|
|
|
import re
|
|
|
|
import ujson
|
|
|
|
|
|
|
|
Call = Dict[str, Any]
|
|
|
|
|
|
|
|
def clean_up_pattern(s):
|
|
|
|
# type: (str) -> str
|
|
|
|
paren_level = 0
|
|
|
|
in_braces = False
|
|
|
|
result = ''
|
|
|
|
prior_char = None
|
|
|
|
for c in s:
|
|
|
|
if c == '(':
|
|
|
|
paren_level += 1
|
|
|
|
if c == '<' and prior_char == 'P':
|
|
|
|
in_braces = True
|
|
|
|
if in_braces or (paren_level == 0):
|
|
|
|
if c != '?':
|
|
|
|
result += c
|
|
|
|
if c == ')':
|
|
|
|
paren_level -= 1
|
|
|
|
if c == '>':
|
|
|
|
in_braces = False
|
|
|
|
prior_char = c
|
|
|
|
return result
|
|
|
|
|
|
|
|
def encode_info(info):
|
|
|
|
# type: (Any) -> str
|
|
|
|
try:
|
|
|
|
result = ''
|
|
|
|
try:
|
|
|
|
info = ujson.loads(info)
|
|
|
|
result = '(stringified)\n'
|
2017-03-05 10:25:27 +01:00
|
|
|
except Exception:
|
2016-11-24 19:45:25 +01:00
|
|
|
pass
|
2016-11-30 22:49:02 +01:00
|
|
|
result += cgi.escape(pprint.pformat(info, indent=4))
|
2016-11-24 19:45:25 +01:00
|
|
|
return '<pre>' + result + '</pre>'
|
2017-03-05 10:25:27 +01:00
|
|
|
except Exception:
|
2016-11-24 19:45:25 +01:00
|
|
|
pass
|
|
|
|
try:
|
|
|
|
return cgi.escape(str(info))
|
2017-03-05 10:25:27 +01:00
|
|
|
except Exception:
|
2016-11-24 19:45:25 +01:00
|
|
|
pass
|
|
|
|
return 'NOT ENCODABLE'
|
|
|
|
|
|
|
|
def fix_test_name(s):
|
|
|
|
# type: (str) -> str
|
|
|
|
return s.replace('zerver.tests.', '')
|
|
|
|
|
|
|
|
def create_single_page(pattern, out_dir, href, calls):
|
|
|
|
# type: (str, str, str, List[Any]) -> None
|
|
|
|
fn = out_dir + '/' + href
|
|
|
|
with open(fn, 'w') as f:
|
|
|
|
f.write('''
|
|
|
|
<style>
|
|
|
|
.test {
|
|
|
|
margin: 20px;
|
|
|
|
}
|
|
|
|
</style>
|
|
|
|
''')
|
|
|
|
f.write('<h3>%s</h3>\n' % (cgi.escape(pattern),))
|
|
|
|
calls.sort(key=lambda call: call['status_code'])
|
|
|
|
for call in calls:
|
|
|
|
f.write('<hr>')
|
|
|
|
f.write('\n%s' % (fix_test_name(call['test_name']),))
|
|
|
|
f.write('<div class="test">')
|
|
|
|
try:
|
|
|
|
f.write(call['url'])
|
2017-03-05 10:25:27 +01:00
|
|
|
except Exception:
|
2016-11-24 19:45:25 +01:00
|
|
|
f.write(call['url'].encode('utf8'))
|
|
|
|
f.write('<br>\n')
|
|
|
|
f.write(call['method'] + '<br>\n')
|
|
|
|
f.write('status code: %s<br>\n' % (call['status_code'],))
|
|
|
|
f.write('<br>')
|
|
|
|
f.write('</div>')
|
|
|
|
|
|
|
|
def create_user_docs():
|
|
|
|
# type: () -> None
|
2017-05-17 22:48:56 +02:00
|
|
|
fn = 'var/url_coverage.txt' # TODO: make path more robust, maybe use json suffix
|
2016-11-24 19:45:25 +01:00
|
|
|
|
|
|
|
out_dir = 'var/api_docs'
|
|
|
|
try:
|
|
|
|
os.mkdir(out_dir)
|
|
|
|
except OSError:
|
|
|
|
pass
|
|
|
|
|
|
|
|
main_page = out_dir + '/index.html'
|
|
|
|
|
|
|
|
with open(main_page, 'w') as f:
|
|
|
|
f.write('''
|
|
|
|
<style>
|
|
|
|
li {
|
|
|
|
list-style-type: none;
|
|
|
|
}
|
|
|
|
|
|
|
|
a {
|
|
|
|
text-decoration: none;
|
|
|
|
}
|
|
|
|
</style>
|
|
|
|
''')
|
|
|
|
|
|
|
|
calls = []
|
|
|
|
for line in open(fn):
|
|
|
|
calls.append(ujson.loads(line))
|
|
|
|
|
2017-05-17 22:48:56 +02:00
|
|
|
pattern_dict = defaultdict(list) # type: Dict[str, List[Call]]
|
2016-11-24 19:45:25 +01:00
|
|
|
for call in calls:
|
|
|
|
if 'pattern' in call:
|
|
|
|
pattern = clean_up_pattern(call['pattern'])
|
|
|
|
if pattern:
|
|
|
|
pattern_dict[pattern].append(call)
|
|
|
|
|
|
|
|
patterns = set(pattern_dict.keys())
|
|
|
|
|
|
|
|
tups = [
|
|
|
|
('api/v1/external', 'webhooks'),
|
|
|
|
('api/v1', 'api'),
|
|
|
|
('json', 'legacy'),
|
|
|
|
]
|
|
|
|
|
2017-05-17 22:48:56 +02:00
|
|
|
groups = dict() # type: Dict[str, Set[str]]
|
2016-11-24 19:45:25 +01:00
|
|
|
for prefix, name in tups:
|
|
|
|
groups[name] = {p for p in patterns if p.startswith(prefix)}
|
|
|
|
patterns -= groups[name]
|
|
|
|
|
|
|
|
groups['other'] = patterns
|
|
|
|
|
|
|
|
for name in ['api', 'legacy', 'webhooks', 'other']:
|
2016-11-28 23:29:01 +01:00
|
|
|
f.write(name + ' endpoints:\n\n')
|
2016-11-24 19:45:25 +01:00
|
|
|
f.write('<ul>\n')
|
|
|
|
for pattern in sorted(groups[name]):
|
|
|
|
href = pattern.replace('/', '-') + '.html'
|
|
|
|
link = '<a href="%s">%s</a>' % (href, cgi.escape(pattern))
|
|
|
|
f.write('<li>' + link + '</li>\n')
|
|
|
|
create_single_page(pattern, out_dir, href, pattern_dict[pattern])
|
|
|
|
f.write('</ul>')
|
|
|
|
f.write('\n')
|
|
|
|
|
|
|
|
print('open %s' % (main_page,))
|
|
|
|
|
|
|
|
|
|
|
|
if __name__ == '__main__':
|
|
|
|
create_user_docs()
|