mirror of
https://github.com/LonamiWebs/Telethon.git
synced 2025-02-16 19:41:07 +03:00
Make complete use of pathlib (fix up 8224e5a
)
This commit is contained in:
parent
8224e5aabf
commit
aefa429236
|
@ -3,25 +3,25 @@ import re
|
|||
|
||||
|
||||
class DocsWriter:
|
||||
"""Utility class used to write the HTML files used on the documentation"""
|
||||
def __init__(self, filename, type_to_path):
|
||||
"""Initializes the writer to the specified output file,
|
||||
creating the parent directories when used if required.
|
||||
|
||||
'type_to_path_function' should be a function which, given a type
|
||||
name and a named argument relative_to, returns the file path for
|
||||
the specified type, relative to the given filename
|
||||
"""
|
||||
Utility class used to write the HTML files used on the documentation.
|
||||
"""
|
||||
def __init__(self, root, filename, type_to_path):
|
||||
"""
|
||||
Initializes the writer to the specified output file,
|
||||
creating the parent directories when used if required.
|
||||
"""
|
||||
self.root = root
|
||||
self.filename = filename
|
||||
self._parent = str(self.filename.parent)
|
||||
self.handle = None
|
||||
self.title = ''
|
||||
|
||||
# Should be set before calling adding items to the menu
|
||||
self.menu_separator_tag = None
|
||||
|
||||
# Utility functions TODO There must be a better way
|
||||
self.type_to_path = lambda t: type_to_path(
|
||||
t, relative_to=self.filename
|
||||
)
|
||||
# Utility functions
|
||||
self.type_to_path = lambda t: self._rel(type_to_path(t))
|
||||
|
||||
# Control signals
|
||||
self.menu_began = False
|
||||
|
@ -30,11 +30,20 @@ class DocsWriter:
|
|||
self.write_copy_script = False
|
||||
self._script = ''
|
||||
|
||||
def _rel(self, path):
|
||||
"""
|
||||
Get the relative path for the given path from the current
|
||||
file by working around https://bugs.python.org/issue20012.
|
||||
"""
|
||||
return os.path.relpath(str(path), self._parent)
|
||||
|
||||
# High level writing
|
||||
def write_head(self, title, relative_css_path, default_css):
|
||||
def write_head(self, title, css_path, default_css):
|
||||
"""Writes the head part for the generated document,
|
||||
with the given title and CSS
|
||||
"""
|
||||
#
|
||||
self.title = title
|
||||
self.write(
|
||||
'''<!DOCTYPE html>
|
||||
<html>
|
||||
|
@ -54,17 +63,17 @@ class DocsWriter:
|
|||
<body>
|
||||
<div id="main_div">''',
|
||||
title=title,
|
||||
rel_css=str(relative_css_path).rstrip('/'),
|
||||
rel_css=self._rel(css_path),
|
||||
def_css=default_css
|
||||
)
|
||||
|
||||
def set_menu_separator(self, relative_image_path):
|
||||
def set_menu_separator(self, img):
|
||||
"""Sets the menu separator.
|
||||
Must be called before adding entries to the menu
|
||||
"""
|
||||
if relative_image_path:
|
||||
self.menu_separator_tag = \
|
||||
'<img src="{}" alt="/" />'.format(relative_image_path)
|
||||
if img:
|
||||
self.menu_separator_tag = '<img src="{}" alt="/" />'.format(
|
||||
self._rel(img))
|
||||
else:
|
||||
self.menu_separator_tag = None
|
||||
|
||||
|
@ -80,7 +89,7 @@ class DocsWriter:
|
|||
|
||||
self.write('<li>')
|
||||
if link:
|
||||
self.write('<a href="{}">', link)
|
||||
self.write('<a href="{}">', self._rel(link))
|
||||
|
||||
# Write the real menu entry text
|
||||
self.write(name)
|
||||
|
@ -210,7 +219,7 @@ class DocsWriter:
|
|||
if bold:
|
||||
self.write('<b>')
|
||||
if link:
|
||||
self.write('<a href="{}">', link)
|
||||
self.write('<a href="{}">', self._rel(link))
|
||||
|
||||
# Finally write the real table data, the given text
|
||||
self.write(text)
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
#!/usr/bin/env python3
|
||||
import functools
|
||||
import os
|
||||
import re
|
||||
import shutil
|
||||
from collections import defaultdict
|
||||
|
@ -35,6 +34,7 @@ def get_import_code(tlobject):
|
|||
|
||||
def _get_create_path_for(root, tlobject, make=True):
|
||||
"""Creates and returns the path for the given TLObject at root."""
|
||||
# TODO Can we pre-create all required directories?
|
||||
out_dir = root / ('methods' if tlobject.is_function else 'constructors')
|
||||
if tlobject.namespace:
|
||||
out_dir /= tlobject.namespace
|
||||
|
@ -45,33 +45,22 @@ def _get_create_path_for(root, tlobject, make=True):
|
|||
return out_dir / _get_file_name(tlobject)
|
||||
|
||||
|
||||
def _get_path_for_type(root, type_, relative_to='.'):
|
||||
def _get_path_for_type(type_):
|
||||
"""Similar to `_get_create_path_for` but for only type names."""
|
||||
if type_.lower() in CORE_TYPES:
|
||||
path = 'index.html#%s' % type_.lower()
|
||||
return Path('index.html#%s' % type_.lower())
|
||||
elif '.' in type_:
|
||||
namespace, name = type_.split('.')
|
||||
path = 'types/%s/%s' % (namespace, _get_file_name(name))
|
||||
return Path('types', namespace, _get_file_name(name))
|
||||
else:
|
||||
path = 'types/%s' % _get_file_name(type_)
|
||||
|
||||
return _get_relative_path(root / path, relative_to)
|
||||
|
||||
|
||||
def _get_relative_path(destination, relative_to, folder=False):
|
||||
"""Return the relative path to destination from relative_to."""
|
||||
relative_to = Path(relative_to)
|
||||
if not folder:
|
||||
relative_to = relative_to.parent
|
||||
|
||||
# TODO Use pathlib here
|
||||
return Path(os.path.relpath(destination, start=relative_to))
|
||||
return Path('types', _get_file_name(type_))
|
||||
|
||||
|
||||
def _find_title(html_file):
|
||||
"""Finds the <title> for the given HTML file, or (Unknown)."""
|
||||
with open(html_file, 'r') as fp:
|
||||
for line in fp:
|
||||
# TODO Is it necessary to read files like this?
|
||||
with html_file.open() as f:
|
||||
for line in f:
|
||||
if '<title>' in line:
|
||||
# + 7 to skip len('<title>')
|
||||
return line[line.index('<title>') + 7:line.index('</title>')]
|
||||
|
@ -79,25 +68,27 @@ def _find_title(html_file):
|
|||
return '(Unknown)'
|
||||
|
||||
|
||||
def _build_menu(docs, filename, root, relative_main_index):
|
||||
"""Builds the menu using the given DocumentWriter up to 'filename',
|
||||
which must be a file (it cannot be a directory)"""
|
||||
filename = _get_relative_path(filename, root)
|
||||
docs.add_menu('API', relative_main_index)
|
||||
def _build_menu(docs):
|
||||
"""
|
||||
Builds the menu used for the current ``DocumentWriter``.
|
||||
"""
|
||||
|
||||
items = str(filename).split('/')
|
||||
for i in range(len(items) - 1):
|
||||
item = items[i]
|
||||
link = '../' * (len(items) - (i + 2))
|
||||
link += 'index.html'
|
||||
docs.add_menu(item.title(), link=link)
|
||||
paths = []
|
||||
current = docs.filename
|
||||
while current != docs.root:
|
||||
current = current.parent
|
||||
paths.append(current)
|
||||
|
||||
for path in reversed(paths):
|
||||
docs.add_menu(path.stem.title(), link=path / 'index.html')
|
||||
|
||||
if docs.filename.stem != 'index':
|
||||
docs.add_menu(docs.title, link=docs.filename)
|
||||
|
||||
if items[-1] != 'index.html':
|
||||
docs.add_menu(os.path.splitext(items[-1])[0])
|
||||
docs.end_menu()
|
||||
|
||||
|
||||
def _generate_index(folder, original_paths, root,
|
||||
def _generate_index(root, folder, paths,
|
||||
bots_index=False, bots_index_paths=()):
|
||||
"""Generates the index file for the specified folder"""
|
||||
# Determine the namespaces listed here (as sub folders)
|
||||
|
@ -107,39 +98,23 @@ def _generate_index(folder, original_paths, root,
|
|||
INDEX = 'index.html'
|
||||
BOT_INDEX = 'botindex.html'
|
||||
|
||||
if not bots_index:
|
||||
for item in folder.iterdir():
|
||||
if item.is_dir():
|
||||
namespaces.append(item)
|
||||
elif item not in (INDEX, BOT_INDEX):
|
||||
files.append(item)
|
||||
else:
|
||||
# bots_index_paths should be a list of "namespace/method.html"
|
||||
# or "method.html"
|
||||
for item in bots_index_paths:
|
||||
dirname = item.parent
|
||||
if dirname and dirname not in namespaces:
|
||||
namespaces.append(dirname)
|
||||
elif not dirname and item not in (INDEX, BOT_INDEX):
|
||||
files.append(item)
|
||||
|
||||
paths = {k: _get_relative_path(v, folder, folder=True)
|
||||
for k, v in original_paths.items()}
|
||||
for item in (bots_index_paths or folder.iterdir()):
|
||||
if item.is_dir():
|
||||
namespaces.append(item)
|
||||
elif item.name not in (INDEX, BOT_INDEX):
|
||||
files.append(item)
|
||||
|
||||
# Now that everything is setup, write the index.html file
|
||||
filename = folder / (BOT_INDEX if bots_index else INDEX)
|
||||
with DocsWriter(filename, type_to_path=_get_path_for_type) as docs:
|
||||
with DocsWriter(root, filename, _get_path_for_type) as docs:
|
||||
# Title should be the current folder name
|
||||
docs.write_head(str(folder).title(),
|
||||
relative_css_path=paths['css'],
|
||||
default_css=original_paths['default_css'])
|
||||
css_path=paths['css'],
|
||||
default_css=paths['default_css'])
|
||||
|
||||
docs.set_menu_separator(paths['arrow'])
|
||||
_build_menu(docs, filename, root,
|
||||
relative_main_index=paths['index_all'])
|
||||
|
||||
docs.write_title(str(
|
||||
_get_relative_path(folder, root, folder=True)).title())
|
||||
_build_menu(docs)
|
||||
docs.write_title(str(filename.parent.relative_to(root)).title())
|
||||
|
||||
if bots_index:
|
||||
docs.write_text('These are the methods that you may be able to '
|
||||
|
@ -157,23 +132,22 @@ def _generate_index(folder, original_paths, root,
|
|||
namespace_paths = []
|
||||
if bots_index:
|
||||
for item in bots_index_paths:
|
||||
# TODO .name? or not
|
||||
if item.parent.name == namespace:
|
||||
namespace_paths.append(item.name)
|
||||
_generate_index(folder / namespace,
|
||||
original_paths, root,
|
||||
if item.parent == namespace:
|
||||
namespace_paths.append(item)
|
||||
|
||||
_generate_index(root, namespace, paths,
|
||||
bots_index, namespace_paths)
|
||||
if bots_index:
|
||||
docs.add_row(namespace.title(), link=namespace / BOT_INDEX)
|
||||
else:
|
||||
docs.add_row(namespace.title(), link=namespace / INDEX)
|
||||
|
||||
docs.add_row(
|
||||
namespace.stem.title(),
|
||||
link=namespace / (BOT_INDEX if bots_index else INDEX))
|
||||
|
||||
docs.end_table()
|
||||
|
||||
docs.write_title('Available items')
|
||||
docs.begin_table(2)
|
||||
|
||||
files = [(f, _find_title(folder / f)) for f in files]
|
||||
files = [(f, _find_title(f)) for f in files]
|
||||
files.sort(key=lambda t: t[1])
|
||||
|
||||
for file, title in files:
|
||||
|
@ -234,7 +208,7 @@ def _copy_replace(src, dst, replacements):
|
|||
))
|
||||
|
||||
|
||||
def _write_html_pages(tlobjects, methods, layer, input_res, output_dir):
|
||||
def _write_html_pages(root, tlobjects, methods, layer, input_res):
|
||||
"""
|
||||
Generates the documentation HTML files from from ``scheme.tl``
|
||||
to ``/methods`` and ``/constructors``, etc.
|
||||
|
@ -242,19 +216,18 @@ def _write_html_pages(tlobjects, methods, layer, input_res, output_dir):
|
|||
# Save 'Type: [Constructors]' for use in both:
|
||||
# * Seeing the return type or constructors belonging to the same type.
|
||||
# * Generating the types documentation, showing available constructors.
|
||||
original_paths = {
|
||||
'css': 'css',
|
||||
'arrow': 'img/arrow.svg',
|
||||
'search.js': 'js/search.js',
|
||||
'404': '404.html',
|
||||
'index_all': 'index.html',
|
||||
'bot_index': 'botindex.html',
|
||||
'index_types': 'types/index.html',
|
||||
'index_methods': 'methods/index.html',
|
||||
'index_constructors': 'constructors/index.html'
|
||||
}
|
||||
original_paths = {k: output_dir / v for k, v in original_paths.items()}
|
||||
original_paths['default_css'] = 'light' # docs.<name>.css, local path
|
||||
paths = {k: root / v for k, v in (
|
||||
('css', 'css'),
|
||||
('arrow', 'img/arrow.svg'),
|
||||
('search.js', 'js/search.js'),
|
||||
('404', '404.html'),
|
||||
('index_all', 'index.html'),
|
||||
('bot_index', 'botindex.html'),
|
||||
('index_types', 'types/index.html'),
|
||||
('index_methods', 'methods/index.html'),
|
||||
('index_constructors', 'constructors/index.html')
|
||||
)}
|
||||
paths['default_css'] = 'light' # docs.<name>.css, local path
|
||||
type_to_constructors = defaultdict(list)
|
||||
type_to_functions = defaultdict(list)
|
||||
for tlobject in tlobjects:
|
||||
|
@ -267,24 +240,20 @@ def _write_html_pages(tlobjects, methods, layer, input_res, output_dir):
|
|||
methods = {m.name: m for m in methods}
|
||||
|
||||
# Since the output directory is needed everywhere partially apply it now
|
||||
create_path_for = functools.partial(_get_create_path_for, output_dir)
|
||||
path_for_type = functools.partial(_get_path_for_type, output_dir)
|
||||
create_path_for = functools.partial(_get_create_path_for, root)
|
||||
path_for_type = lambda t: root / _get_path_for_type(t)
|
||||
bot_docs_paths = []
|
||||
|
||||
for tlobject in tlobjects:
|
||||
filename = create_path_for(tlobject)
|
||||
paths = {k: _get_relative_path(v, filename)
|
||||
for k, v in original_paths.items()}
|
||||
|
||||
with DocsWriter(filename, type_to_path=path_for_type) as docs:
|
||||
with DocsWriter(root, filename, path_for_type) as docs:
|
||||
docs.write_head(title=tlobject.class_name,
|
||||
relative_css_path=paths['css'],
|
||||
default_css=original_paths['default_css'])
|
||||
css_path=paths['css'],
|
||||
default_css=paths['default_css'])
|
||||
|
||||
# Create the menu (path to the current TLObject)
|
||||
docs.set_menu_separator(paths['arrow'])
|
||||
_build_menu(docs, filename, output_dir,
|
||||
relative_main_index=paths['index_all'])
|
||||
_build_menu(docs)
|
||||
|
||||
# Create the page title
|
||||
docs.write_title(tlobject.class_name)
|
||||
|
@ -334,9 +303,7 @@ def _write_html_pages(tlobjects, methods, layer, input_res, output_dir):
|
|||
inner = tlobject.result
|
||||
|
||||
docs.begin_table(column_count=1)
|
||||
docs.add_row(inner, link=path_for_type(
|
||||
inner, relative_to=filename
|
||||
))
|
||||
docs.add_row(inner, link=path_for_type(inner))
|
||||
docs.end_table()
|
||||
|
||||
cs = type_to_constructors.get(inner, [])
|
||||
|
@ -350,7 +317,6 @@ def _write_html_pages(tlobjects, methods, layer, input_res, output_dir):
|
|||
docs.begin_table(column_count=2)
|
||||
for constructor in cs:
|
||||
link = create_path_for(constructor)
|
||||
link = _get_relative_path(link, relative_to=filename)
|
||||
docs.add_row(constructor.class_name, link=link)
|
||||
docs.end_table()
|
||||
|
||||
|
@ -381,8 +347,8 @@ def _write_html_pages(tlobjects, methods, layer, input_res, output_dir):
|
|||
docs.add_row('!' + friendly_type, align='center')
|
||||
else:
|
||||
docs.add_row(
|
||||
friendly_type, align='center', link=
|
||||
path_for_type(arg.type, relative_to=filename)
|
||||
friendly_type, align='center',
|
||||
link=path_for_type(arg.type)
|
||||
)
|
||||
|
||||
# Add a description for this argument
|
||||
|
@ -442,12 +408,6 @@ def _write_html_pages(tlobjects, methods, layer, input_res, output_dir):
|
|||
docs.add_script(relative_src=paths['search.js'])
|
||||
docs.end_body()
|
||||
|
||||
temp = []
|
||||
for item in bot_docs_paths:
|
||||
# TODO What?
|
||||
temp.append(os.path.sep.join(str(item).split(os.path.sep)[2:]))
|
||||
bot_docs_paths = temp
|
||||
|
||||
# Find all the available types (which are not the same as the constructors)
|
||||
# Each type has a list of constructors associated to it, hence is a map
|
||||
for t, cs in type_to_constructors.items():
|
||||
|
@ -462,17 +422,13 @@ def _write_html_pages(tlobjects, methods, layer, input_res, output_dir):
|
|||
else:
|
||||
namespace, name = None, t
|
||||
|
||||
paths = {k: _get_relative_path(v, out_dir, folder=True)
|
||||
for k, v in original_paths.items()}
|
||||
|
||||
with DocsWriter(filename, type_to_path=path_for_type) as docs:
|
||||
with DocsWriter(root, filename, path_for_type) as docs:
|
||||
docs.write_head(title=snake_to_camel_case(name),
|
||||
relative_css_path=paths['css'],
|
||||
default_css=original_paths['default_css'])
|
||||
css_path=paths['css'],
|
||||
default_css=paths['default_css'])
|
||||
|
||||
docs.set_menu_separator(paths['arrow'])
|
||||
_build_menu(docs, filename, output_dir,
|
||||
relative_main_index=paths['index_all'])
|
||||
_build_menu(docs)
|
||||
|
||||
# Main file title
|
||||
docs.write_title(snake_to_camel_case(name))
|
||||
|
@ -491,7 +447,6 @@ def _write_html_pages(tlobjects, methods, layer, input_res, output_dir):
|
|||
for constructor in cs:
|
||||
# Constructor full name
|
||||
link = create_path_for(constructor)
|
||||
link = _get_relative_path(link, relative_to=filename)
|
||||
docs.add_row(constructor.class_name, link=link)
|
||||
docs.end_table()
|
||||
|
||||
|
@ -511,7 +466,6 @@ def _write_html_pages(tlobjects, methods, layer, input_res, output_dir):
|
|||
docs.begin_table(2)
|
||||
for func in functions:
|
||||
link = create_path_for(func)
|
||||
link = _get_relative_path(link, relative_to=filename)
|
||||
docs.add_row(func.class_name, link=link)
|
||||
docs.end_table()
|
||||
|
||||
|
@ -536,7 +490,6 @@ def _write_html_pages(tlobjects, methods, layer, input_res, output_dir):
|
|||
docs.begin_table(2)
|
||||
for ot in other_methods:
|
||||
link = create_path_for(ot)
|
||||
link = _get_relative_path(link, relative_to=filename)
|
||||
docs.add_row(ot.class_name, link=link)
|
||||
docs.end_table()
|
||||
|
||||
|
@ -562,7 +515,6 @@ def _write_html_pages(tlobjects, methods, layer, input_res, output_dir):
|
|||
docs.begin_table(2)
|
||||
for ot in other_types:
|
||||
link = create_path_for(ot)
|
||||
link = _get_relative_path(link, relative_to=filename)
|
||||
docs.add_row(ot.class_name, link=link)
|
||||
docs.end_table()
|
||||
docs.end_body()
|
||||
|
@ -572,11 +524,10 @@ def _write_html_pages(tlobjects, methods, layer, input_res, output_dir):
|
|||
# information that we have available, simply a file listing all the others
|
||||
# accessible by clicking on their title
|
||||
for folder in ['types', 'methods', 'constructors']:
|
||||
_generate_index(output_dir / folder, original_paths,
|
||||
output_dir)
|
||||
_generate_index(root, root / folder, paths)
|
||||
|
||||
_generate_index(output_dir / 'methods', original_paths,
|
||||
output_dir, True, bot_docs_paths)
|
||||
_generate_index(root, root / 'methods', paths, True,
|
||||
bot_docs_paths)
|
||||
|
||||
# Write the final core index, the main index for the rest of files
|
||||
types = set()
|
||||
|
@ -598,9 +549,8 @@ def _write_html_pages(tlobjects, methods, layer, input_res, output_dir):
|
|||
methods = sorted(methods, key=lambda m: m.name)
|
||||
cs = sorted(cs, key=lambda c: c.name)
|
||||
|
||||
shutil.copy(str(input_res / '404.html'), str(original_paths['404']))
|
||||
_copy_replace(input_res / 'core.html',
|
||||
original_paths['index_all'], {
|
||||
shutil.copy(str(input_res / '404.html'), str(paths['404']))
|
||||
_copy_replace(input_res / 'core.html', paths['index_all'], {
|
||||
'{type_count}': len(types),
|
||||
'{method_count}': len(methods),
|
||||
'{constructor_count}': len(tlobjects) - len(methods),
|
||||
|
@ -626,15 +576,15 @@ def _write_html_pages(tlobjects, methods, layer, input_res, output_dir):
|
|||
type_names = fmt(types, formatter=lambda x: x)
|
||||
|
||||
# Local URLs shouldn't rely on the output's root, so set empty root
|
||||
create_path_for = functools.partial(_get_create_path_for, '', make=False)
|
||||
path_for_type = functools.partial(_get_path_for_type, '')
|
||||
create_path_for = functools.partial(
|
||||
_get_create_path_for, Path(), make=False)
|
||||
|
||||
request_urls = fmt(methods, create_path_for)
|
||||
type_urls = fmt(types, path_for_type)
|
||||
type_urls = fmt(types, _get_path_for_type)
|
||||
constructor_urls = fmt(cs, create_path_for)
|
||||
|
||||
original_paths['search.js'].parent.mkdir(parents=True, exist_ok=True)
|
||||
_copy_replace(input_res / 'js/search.js',
|
||||
original_paths['search.js'], {
|
||||
paths['search.js'].parent.mkdir(parents=True, exist_ok=True)
|
||||
_copy_replace(input_res / 'js/search.js', paths['search.js'], {
|
||||
'{request_names}': request_names,
|
||||
'{type_names}': type_names,
|
||||
'{constructor_names}': constructor_names,
|
||||
|
@ -655,5 +605,5 @@ def _copy_resources(res_dir, out_dir):
|
|||
|
||||
def generate_docs(tlobjects, methods, layer, input_res, output_dir):
|
||||
output_dir.mkdir(parents=True, exist_ok=True)
|
||||
_write_html_pages(tlobjects, methods, layer, input_res, output_dir)
|
||||
_write_html_pages(output_dir, tlobjects, methods, layer, input_res)
|
||||
_copy_resources(input_res, output_dir)
|
||||
|
|
Loading…
Reference in New Issue
Block a user