--- a/setup.py +++ b/setup.py @@ -12,10 +12,11 @@ import itertools import json -import os import re import shutil -from codecs import open +from os import chdir +from pathlib import Path +from subprocess import run from sys import argv from setuptools import find_packages, setup @@ -29,30 +30,29 @@ self.original = None def __enter__(self): - self.original = os.path.abspath(os.path.curdir) - os.chdir(os.path.abspath(os.path.dirname(__file__))) + self.original = Path('.') + chdir(str(Path(__file__).parent)) return self def __exit__(self, *args): - os.chdir(self.original) + chdir(str(self.original)) -GENERATOR_DIR = 'telethon_generator' -LIBRARY_DIR = 'telethon' +GENERATOR_DIR = Path('telethon_generator') +LIBRARY_DIR = Path('telethon') -ERRORS_IN_JSON = os.path.join(GENERATOR_DIR, 'data', 'errors.json') -ERRORS_IN_DESC = os.path.join(GENERATOR_DIR, 'data', 'error_descriptions') -ERRORS_OUT = os.path.join(LIBRARY_DIR, 'errors', 'rpcerrorlist.py') +ERRORS_IN_JSON = GENERATOR_DIR / 'data/errors.json' +ERRORS_IN_DESC = GENERATOR_DIR / 'data/error_descriptions' +ERRORS_OUT = LIBRARY_DIR / 'errors/rpcerrorlist.py' -INVALID_BM_IN = os.path.join(GENERATOR_DIR, 'data', 'invalid_bot_methods.json') +INVALID_BM_IN = GENERATOR_DIR / 'data/invalid_bot_methods.json' -TLOBJECT_IN_CORE_TL = os.path.join(GENERATOR_DIR, 'data', 'mtproto_api.tl') -TLOBJECT_IN_TL = os.path.join(GENERATOR_DIR, 'data', 'telegram_api.tl') -TLOBJECT_OUT = os.path.join(LIBRARY_DIR, 'tl') +TLOBJECT_IN_TLS = [Path(x) for x in GENERATOR_DIR.glob('data/*.tl')] +TLOBJECT_OUT = LIBRARY_DIR / 'tl' IMPORT_DEPTH = 2 -DOCS_IN_RES = os.path.join(GENERATOR_DIR, 'data', 'html') -DOCS_OUT = 'docs' +DOCS_IN_RES = GENERATOR_DIR / 'data/html' +DOCS_OUT = Path('docs') def generate(which): @@ -60,15 +60,12 @@ from telethon_generator.generators import\ generate_errors, generate_tlobjects, generate_docs, clean_tlobjects - # Older Python versions open the file as bytes instead (3.4.2) - with open(INVALID_BM_IN, 'r') as f: + with INVALID_BM_IN.open('r') as f: invalid_bot_methods = set(json.load(f)) - - layer = find_layer(TLOBJECT_IN_TL) + layer = next(filter(None, map(find_layer, TLOBJECT_IN_TLS))) errors = list(parse_errors(ERRORS_IN_JSON, ERRORS_IN_DESC)) - tlobjects = list(itertools.chain( - parse_tl(TLOBJECT_IN_CORE_TL, layer, invalid_bot_methods), - parse_tl(TLOBJECT_IN_TL, layer, invalid_bot_methods))) + tlobjects = list(itertools.chain(*( + parse_tl(file, layer, invalid_bot_methods) for file in TLOBJECT_IN_TLS))) if not which: which.extend(('tl', 'errors')) @@ -96,30 +93,29 @@ which.remove('errors') print(action, 'RPCErrors...') if clean: - if os.path.isfile(ERRORS_OUT): - os.remove(ERRORS_OUT) + if ERRORS_OUT.is_file(): + ERRORS_OUT.unlink() else: - with open(ERRORS_OUT, 'w', encoding='utf-8') as file: + with ERRORS_OUT.open('w') as file: generate_errors(errors, file) if 'docs' in which: which.remove('docs') print(action, 'documentation...') if clean: - if os.path.isdir(DOCS_OUT): - shutil.rmtree(DOCS_OUT) + if DOCS_OUT.is_dir(): + shutil.rmtree(str(DOCS_OUT)) else: generate_docs(tlobjects, methods, layer, DOCS_IN_RES, DOCS_OUT) if 'json' in which: which.remove('json') print(action, 'JSON schema...') - mtproto = 'mtproto_api.json' - telegram = 'telegram_api.json' + json_files = [x.with_suffix('.json') for x in TLOBJECT_IN_TLS] if clean: - for x in (mtproto, telegram): - if os.path.isfile(x): - os.remove(x) + for file in json_files: + if file.is_file(): + file.unlink() else: def gen_json(fin, fout): methods = [] @@ -131,8 +130,8 @@ with open(fout, 'w') as f: json.dump(what, f, indent=2) - gen_json(TLOBJECT_IN_CORE_TL, mtproto) - gen_json(TLOBJECT_IN_TL, telegram) + for fin, fout in zip(TLOBJECT_IN_TLS, json_files): + gen_json(fin, fout) if which: print('The following items were not understood:', which) @@ -156,22 +155,17 @@ print('Packaging for PyPi aborted, importing the module failed.') return - # Need python3.5 or higher, but Telethon is supposed to support 3.x - # Place it here since noone should be running ./setup.py pypi anyway - from subprocess import run - from shutil import rmtree - for x in ('build', 'dist', 'Telethon.egg-info'): - rmtree(x, ignore_errors=True) + shutil.rmtree(x, ignore_errors=True) run('python3 setup.py sdist', shell=True) run('python3 setup.py bdist_wheel', shell=True) run('twine upload dist/*', shell=True) for x in ('build', 'dist', 'Telethon.egg-info'): - rmtree(x, ignore_errors=True) + shutil.rmtree(x, ignore_errors=True) else: # e.g. install from GitHub - if os.path.isdir(GENERATOR_DIR): + if GENERATOR_DIR.is_dir(): generate(['tl', 'errors']) # Get the long description from the README file --- a/telethon_generator/docswriter.py +++ b/telethon_generator/docswriter.py @@ -2,0 +2,0 @@ 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 @@ 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( ''' @@ -54,17 +63,17 @@