Telethon/setup.py

264 lines
8.7 KiB
Python
Raw Normal View History

2017-06-21 20:18:22 +03:00
#!/usr/bin/env python3
"""A setuptools based setup module.
See:
https://packaging.python.org/en/latest/distributing.html
https://github.com/pypa/sampleproject
Extra supported commands are:
2018-04-15 14:19:25 +03:00
* gen, to generate the classes required for Telethon to run or docs
* pypi, to generate sdist, bdist_wheel, and push to PyPi
"""
import itertools
2018-06-06 18:35:06 +03:00
import json
import os
2018-04-15 14:19:25 +03:00
import re
import shutil
2020-10-05 11:47:28 +03:00
import sys
import urllib.request
2018-12-15 15:26:18 +03:00
from pathlib import Path
from subprocess import run
2016-11-30 00:29:42 +03:00
from setuptools import find_packages, setup
2020-10-05 11:47:28 +03:00
# Needed since we're importing local files
sys.path.insert(0, os.path.dirname(__file__))
class TempWorkDir:
"""Switches the working directory to be the one on which this file lives,
while within the 'with' block.
"""
def __init__(self, new=None):
self.original = None
self.new = new or str(Path(__file__).parent.resolve())
def __enter__(self):
# os.chdir does not work with Path in Python 3.5.x
self.original = str(Path('.').resolve())
os.makedirs(self.new, exist_ok=True)
os.chdir(self.new)
return self
def __exit__(self, *args):
os.chdir(self.original)
API_REF_URL = 'https://tl.telethon.dev/'
2018-12-15 15:26:18 +03:00
GENERATOR_DIR = Path('telethon_generator')
LIBRARY_DIR = Path('telethon')
2018-12-15 15:26:18 +03:00
ERRORS_IN = GENERATOR_DIR / 'data/errors.csv'
ERRORS_OUT = LIBRARY_DIR / 'errors/rpcerrorlist.py'
2018-12-15 15:26:18 +03:00
METHODS_IN = GENERATOR_DIR / 'data/methods.csv'
2018-06-06 18:35:06 +03:00
# Which raw API methods are covered by *friendly* methods in the client?
FRIENDLY_IN = GENERATOR_DIR / 'data/friendly.csv'
2018-12-15 15:26:18 +03:00
TLOBJECT_IN_TLS = [Path(x) for x in GENERATOR_DIR.glob('data/*.tl')]
TLOBJECT_OUT = LIBRARY_DIR / 'tl'
2018-04-15 14:19:25 +03:00
IMPORT_DEPTH = 2
2018-12-15 15:26:18 +03:00
DOCS_IN_RES = GENERATOR_DIR / 'data/html'
DOCS_OUT = Path('docs')
2018-04-15 14:19:25 +03:00
def generate(which, action='gen'):
from telethon_generator.parsers import\
parse_errors, parse_methods, parse_tl, find_layer
2018-04-15 14:19:25 +03:00
from telethon_generator.generators import\
generate_errors, generate_tlobjects, generate_docs, clean_tlobjects
2018-12-15 15:26:18 +03:00
layer = next(filter(None, map(find_layer, TLOBJECT_IN_TLS)))
errors = list(parse_errors(ERRORS_IN))
methods = list(parse_methods(METHODS_IN, FRIENDLY_IN, {e.str_code: e for e in errors}))
2018-12-15 15:26:18 +03:00
tlobjects = list(itertools.chain(*(
parse_tl(file, layer, methods) for file in TLOBJECT_IN_TLS)))
2018-04-15 14:19:25 +03:00
if not which:
which.extend(('tl', 'errors'))
2018-04-15 14:19:25 +03:00
2019-05-04 18:51:14 +03:00
clean = action == 'clean'
2018-04-15 14:19:25 +03:00
action = 'Cleaning' if clean else 'Generating'
if 'all' in which:
which.remove('all')
for x in ('tl', 'errors', 'docs'):
if x not in which:
which.append(x)
if 'tl' in which:
which.remove('tl')
print(action, 'TLObjects...')
if clean:
clean_tlobjects(TLOBJECT_OUT)
else:
generate_tlobjects(tlobjects, layer, IMPORT_DEPTH, TLOBJECT_OUT)
if 'errors' in which:
which.remove('errors')
print(action, 'RPCErrors...')
if clean:
2018-12-15 15:26:18 +03:00
if ERRORS_OUT.is_file():
ERRORS_OUT.unlink()
2018-04-15 14:19:25 +03:00
else:
with ERRORS_OUT.open('w') as file:
2018-04-15 14:19:25 +03:00
generate_errors(errors, file)
if 'docs' in which:
which.remove('docs')
print(action, 'documentation...')
if clean:
2018-12-15 15:26:18 +03:00
if DOCS_OUT.is_dir():
shutil.rmtree(str(DOCS_OUT))
2018-04-15 14:19:25 +03:00
else:
in_path = DOCS_IN_RES.resolve()
with TempWorkDir(DOCS_OUT):
generate_docs(tlobjects, methods, layer, in_path)
2018-04-15 14:19:25 +03:00
2018-06-13 11:48:35 +03:00
if 'json' in which:
which.remove('json')
print(action, 'JSON schema...')
2018-12-15 15:26:18 +03:00
json_files = [x.with_suffix('.json') for x in TLOBJECT_IN_TLS]
2018-06-13 11:48:35 +03:00
if clean:
2018-12-15 15:26:18 +03:00
for file in json_files:
if file.is_file():
file.unlink()
2018-06-13 11:48:35 +03:00
else:
def gen_json(fin, fout):
2019-05-03 14:59:17 +03:00
meths = []
2018-06-13 11:48:35 +03:00
constructors = []
for tl in parse_tl(fin, layer):
if tl.is_function:
2019-05-03 14:59:17 +03:00
meths.append(tl.to_dict())
2018-06-13 11:48:35 +03:00
else:
constructors.append(tl.to_dict())
2019-05-03 14:59:17 +03:00
what = {'constructors': constructors, 'methods': meths}
2018-06-13 11:48:35 +03:00
with open(fout, 'w') as f:
json.dump(what, f, indent=2)
2019-05-03 14:59:17 +03:00
for fs in zip(TLOBJECT_IN_TLS, json_files):
gen_json(*fs)
2018-06-13 11:48:35 +03:00
2018-04-15 14:19:25 +03:00
if which:
print(
'The following items were not understood:', which,
'\n Consider using only "tl", "errors" and/or "docs".'
'\n Using only "clean" will clean them. "all" to act on all.'
'\n For instance "gen tl errors".'
)
2020-10-05 11:47:28 +03:00
def main(argv):
2019-05-04 18:51:14 +03:00
if len(argv) >= 2 and argv[1] in ('gen', 'clean'):
generate(argv[2:], argv[1])
elif len(argv) >= 2 and argv[1] == 'pypi':
# Make sure tl.telethon.dev is up-to-date first
with urllib.request.urlopen(API_REF_URL) as resp:
html = resp.read()
m = re.search(br'layer\s+(\d+)', html)
if not m:
print('Failed to check that the API reference is up to date:', API_REF_URL)
return
from telethon_generator.parsers import find_layer
layer = next(filter(None, map(find_layer, TLOBJECT_IN_TLS)))
published_layer = int(m[1])
if published_layer != layer:
print('Published layer', published_layer, 'does not match current layer', layer, '.')
print('Make sure to update the API reference site first:', API_REF_URL)
return
# (Re)generate the code to make sure we don't push without it
generate(['tl', 'errors'])
# Try importing the telethon module to assert it has no errors
try:
import telethon
except:
print('Packaging for PyPi aborted, importing the module failed.')
return
remove_dirs = ['__pycache__', 'build', 'dist', 'Telethon.egg-info']
for root, _dirs, _files in os.walk(LIBRARY_DIR, topdown=False):
# setuptools is including __pycache__ for some reason (#1605)
if root.endswith('/__pycache__'):
remove_dirs.append(root)
for x in remove_dirs:
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'):
shutil.rmtree(x, ignore_errors=True)
else:
2018-04-15 14:19:25 +03:00
# e.g. install from GitHub
2018-12-15 15:26:18 +03:00
if GENERATOR_DIR.is_dir():
generate(['tl', 'errors'])
# Get the long description from the README file
with open('README.rst', 'r', encoding='utf-8') as f:
long_description = f.read()
with open('telethon/version.py', 'r', encoding='utf-8') as f:
version = re.search(r"^__version__\s*=\s*'(.*)'.*$",
f.read(), flags=re.MULTILINE).group(1)
setup(
2018-06-27 14:05:19 +03:00
name='Telethon',
version=version,
description="Full-featured Telegram client library for Python 3",
long_description=long_description,
url='https://github.com/LonamiWebs/Telethon',
download_url='https://github.com/LonamiWebs/Telethon/releases',
author='Lonami Exo',
author_email='totufals@hotmail.com',
license='MIT',
2018-04-04 11:21:55 +03:00
# See https://stackoverflow.com/a/40300957/4759433
# -> https://www.python.org/dev/peps/pep-0345/#requires-python
# -> http://setuptools.readthedocs.io/en/latest/setuptools.html
2018-06-21 11:47:52 +03:00
python_requires='>=3.5',
2018-04-04 11:21:55 +03:00
# See https://pypi.python.org/pypi?%3Aaction=list_classifiers
classifiers=[
# 3 - Alpha
# 4 - Beta
# 5 - Production/Stable
2018-06-27 14:05:19 +03:00
'Development Status :: 5 - Production/Stable',
'Intended Audience :: Developers',
'Topic :: Communications :: Chat',
'License :: OSI Approved :: MIT License',
'Programming Language :: Python :: 3',
'Programming Language :: Python :: 3.5',
'Programming Language :: Python :: 3.6',
'Programming Language :: Python :: 3.7',
'Programming Language :: Python :: 3.8',
],
keywords='telegram api chat client library messaging mtproto',
packages=find_packages(exclude=[
'telethon_*', 'tests*'
]),
install_requires=['pyaes', 'rsa'],
extras_require={
2018-03-17 19:38:16 +03:00
'cryptg': ['cryptg']
}
)
if __name__ == '__main__':
with TempWorkDir():
2020-10-05 11:47:28 +03:00
main(sys.argv)