mirror of
https://github.com/python-telegram-bot/python-telegram-bot.git
synced 2024-11-28 19:22:54 +01:00
Apply pre-commit
Checks More Widely (#4135)
This commit is contained in:
parent
9c263fbd1a
commit
26f943771b
17 changed files with 104 additions and 100 deletions
|
@ -10,7 +10,6 @@ repos:
|
||||||
hooks:
|
hooks:
|
||||||
- id: ruff
|
- id: ruff
|
||||||
name: ruff
|
name: ruff
|
||||||
files: ^(telegram|examples|tests)/.*\.py$
|
|
||||||
additional_dependencies:
|
additional_dependencies:
|
||||||
- httpx~=0.27.0
|
- httpx~=0.27.0
|
||||||
- tornado~=6.4
|
- tornado~=6.4
|
||||||
|
@ -32,7 +31,7 @@ repos:
|
||||||
rev: v3.0.3
|
rev: v3.0.3
|
||||||
hooks:
|
hooks:
|
||||||
- id: pylint
|
- id: pylint
|
||||||
files: ^(telegram|examples)/.*\.py$
|
files: ^(?!(tests|docs)).*\.py$
|
||||||
additional_dependencies:
|
additional_dependencies:
|
||||||
- httpx~=0.27.0
|
- httpx~=0.27.0
|
||||||
- tornado~=6.4
|
- tornado~=6.4
|
||||||
|
@ -45,7 +44,7 @@ repos:
|
||||||
hooks:
|
hooks:
|
||||||
- id: mypy
|
- id: mypy
|
||||||
name: mypy-ptb
|
name: mypy-ptb
|
||||||
files: ^telegram/.*\.py$
|
files: ^(?!(tests|examples|docs)).*\.py$
|
||||||
additional_dependencies:
|
additional_dependencies:
|
||||||
- types-pytz
|
- types-pytz
|
||||||
- types-cryptography
|
- types-cryptography
|
||||||
|
@ -71,7 +70,6 @@ repos:
|
||||||
rev: v3.15.0
|
rev: v3.15.0
|
||||||
hooks:
|
hooks:
|
||||||
- id: pyupgrade
|
- id: pyupgrade
|
||||||
files: ^(telegram|examples|tests|docs)/.*\.py$
|
|
||||||
args:
|
args:
|
||||||
- --py38-plus
|
- --py38-plus
|
||||||
- repo: https://github.com/pycqa/isort
|
- repo: https://github.com/pycqa/isort
|
||||||
|
|
|
@ -108,12 +108,12 @@ You can also install ``python-telegram-bot-raw`` from source, though this is usu
|
||||||
|
|
||||||
$ git clone https://github.com/python-telegram-bot/python-telegram-bot
|
$ git clone https://github.com/python-telegram-bot/python-telegram-bot
|
||||||
$ cd python-telegram-bot
|
$ cd python-telegram-bot
|
||||||
$ python setup-raw.py install
|
$ python setup_raw.py install
|
||||||
|
|
||||||
Note
|
Note
|
||||||
----
|
----
|
||||||
|
|
||||||
Installing the ``.tar.gz`` archive available on PyPi directly via ``pip`` will *not* work as expected, as ``pip`` does not recognize that it should use ``setup-raw.py`` instead of ``setup.py``.
|
Installing the ``.tar.gz`` archive available on PyPi directly via ``pip`` will *not* work as expected, as ``pip`` does not recognize that it should use ``setup_raw.py`` instead of ``setup.py``.
|
||||||
|
|
||||||
Verifying Releases
|
Verifying Releases
|
||||||
------------------
|
------------------
|
||||||
|
|
0
docs/__init__.py
Normal file
0
docs/__init__.py
Normal file
0
docs/auxil/__init__.py
Normal file
0
docs/auxil/__init__.py
Normal file
|
@ -64,7 +64,7 @@ class AdmonitionInserter:
|
||||||
ForwardRef('DefaultValue[DVValueType]')
|
ForwardRef('DefaultValue[DVValueType]')
|
||||||
"""
|
"""
|
||||||
|
|
||||||
METHOD_NAMES_FOR_BOT_AND_APPBUILDER: dict[type, str] = {
|
METHOD_NAMES_FOR_BOT_AND_APPBUILDER: typing.ClassVar[dict[type, str]] = {
|
||||||
cls: tuple(m[0] for m in _iter_own_public_methods(cls)) # m[0] means we take only names
|
cls: tuple(m[0] for m in _iter_own_public_methods(cls)) # m[0] means we take only names
|
||||||
for cls in (telegram.Bot, telegram.ext.ApplicationBuilder)
|
for cls in (telegram.Bot, telegram.ext.ApplicationBuilder)
|
||||||
}
|
}
|
||||||
|
@ -159,7 +159,7 @@ class AdmonitionInserter:
|
||||||
telegram.ext, inspect.isclass
|
telegram.ext, inspect.isclass
|
||||||
)
|
)
|
||||||
|
|
||||||
for class_name, inspected_class in classes_to_inspect:
|
for _class_name, inspected_class in classes_to_inspect:
|
||||||
# We need to make "<class 'telegram._files.sticker.StickerSet'>" into
|
# We need to make "<class 'telegram._files.sticker.StickerSet'>" into
|
||||||
# "telegram.StickerSet" because that's the way the classes are mentioned in
|
# "telegram.StickerSet" because that's the way the classes are mentioned in
|
||||||
# docstrings.
|
# docstrings.
|
||||||
|
@ -197,8 +197,8 @@ class AdmonitionInserter:
|
||||||
"Error generating Sphinx 'Available in' admonition "
|
"Error generating Sphinx 'Available in' admonition "
|
||||||
f"(admonition_inserter.py). Class {name_of_class_in_attr} present in "
|
f"(admonition_inserter.py). Class {name_of_class_in_attr} present in "
|
||||||
f"attribute {target_attr} of class {name_of_inspected_class_in_docstr}"
|
f"attribute {target_attr} of class {name_of_inspected_class_in_docstr}"
|
||||||
f" could not be resolved. {str(e)}"
|
f" could not be resolved. {e!s}"
|
||||||
)
|
) from e
|
||||||
|
|
||||||
# Properties need to be parsed separately because they act like attributes but not
|
# Properties need to be parsed separately because they act like attributes but not
|
||||||
# listed as attributes.
|
# listed as attributes.
|
||||||
|
@ -240,8 +240,8 @@ class AdmonitionInserter:
|
||||||
"Error generating Sphinx 'Available in' admonition "
|
"Error generating Sphinx 'Available in' admonition "
|
||||||
f"(admonition_inserter.py). Class {name_of_class_in_prop} present in "
|
f"(admonition_inserter.py). Class {name_of_class_in_prop} present in "
|
||||||
f"property {prop_name} of class {name_of_inspected_class_in_docstr}"
|
f"property {prop_name} of class {name_of_inspected_class_in_docstr}"
|
||||||
f" could not be resolved. {str(e)}"
|
f" could not be resolved. {e!s}"
|
||||||
)
|
) from e
|
||||||
|
|
||||||
return self._generate_admonitions(attrs_for_class, admonition_type="available_in")
|
return self._generate_admonitions(attrs_for_class, admonition_type="available_in")
|
||||||
|
|
||||||
|
@ -271,8 +271,8 @@ class AdmonitionInserter:
|
||||||
raise NotImplementedError(
|
raise NotImplementedError(
|
||||||
"Error generating Sphinx 'Returned in' admonition "
|
"Error generating Sphinx 'Returned in' admonition "
|
||||||
f"(admonition_inserter.py). {cls}, method {method_name}. "
|
f"(admonition_inserter.py). {cls}, method {method_name}. "
|
||||||
f"Couldn't resolve type hint in return annotation {ret_annot}. {str(e)}"
|
f"Couldn't resolve type hint in return annotation {ret_annot}. {e!s}"
|
||||||
)
|
) from e
|
||||||
|
|
||||||
return self._generate_admonitions(methods_for_class, admonition_type="returned_in")
|
return self._generate_admonitions(methods_for_class, admonition_type="returned_in")
|
||||||
|
|
||||||
|
@ -297,7 +297,7 @@ class AdmonitionInserter:
|
||||||
|
|
||||||
# inspect methods of all telegram classes for return statements that indicate
|
# inspect methods of all telegram classes for return statements that indicate
|
||||||
# that this given method is a shortcut for a Bot method
|
# that this given method is a shortcut for a Bot method
|
||||||
for class_name, cls in inspect.getmembers(telegram, predicate=inspect.isclass):
|
for _class_name, cls in inspect.getmembers(telegram, predicate=inspect.isclass):
|
||||||
# no need to inspect Bot's own methods, as Bot can't have shortcuts in Bot
|
# no need to inspect Bot's own methods, as Bot can't have shortcuts in Bot
|
||||||
if cls is telegram.Bot:
|
if cls is telegram.Bot:
|
||||||
continue
|
continue
|
||||||
|
@ -344,8 +344,8 @@ class AdmonitionInserter:
|
||||||
raise NotImplementedError(
|
raise NotImplementedError(
|
||||||
"Error generating Sphinx 'Use in' admonition "
|
"Error generating Sphinx 'Use in' admonition "
|
||||||
f"(admonition_inserter.py). {cls}, method {method_name}, parameter "
|
f"(admonition_inserter.py). {cls}, method {method_name}, parameter "
|
||||||
f"{param}: Couldn't resolve type hint {param.annotation}. {str(e)}"
|
f"{param}: Couldn't resolve type hint {param.annotation}. {e!s}"
|
||||||
)
|
) from e
|
||||||
|
|
||||||
return self._generate_admonitions(methods_for_class, admonition_type="use_in")
|
return self._generate_admonitions(methods_for_class, admonition_type="use_in")
|
||||||
|
|
||||||
|
@ -359,17 +359,19 @@ class AdmonitionInserter:
|
||||||
If no key phrases are found, the admonition will be inserted at the very end.
|
If no key phrases are found, the admonition will be inserted at the very end.
|
||||||
"""
|
"""
|
||||||
for idx, value in list(enumerate(lines)):
|
for idx, value in list(enumerate(lines)):
|
||||||
if (
|
if value.startswith(
|
||||||
value.startswith(".. seealso:")
|
(
|
||||||
|
".. seealso:",
|
||||||
# The docstring contains heading "Examples:", but Sphinx will have it converted
|
# The docstring contains heading "Examples:", but Sphinx will have it converted
|
||||||
# to ".. admonition: Examples":
|
# to ".. admonition: Examples":
|
||||||
or value.startswith(".. admonition:: Examples")
|
".. admonition:: Examples",
|
||||||
or value.startswith(".. version")
|
".. version",
|
||||||
# The space after ":param" is important because docstring can contain ":paramref:"
|
# The space after ":param" is important because docstring can contain
|
||||||
# in its plain text in the beginning of a line (e.g. ExtBot):
|
# ":paramref:" in its plain text in the beginning of a line (e.g. ExtBot):
|
||||||
or value.startswith(":param ")
|
":param ",
|
||||||
# some classes (like "Credentials") have no params, so insert before attrs:
|
# some classes (like "Credentials") have no params, so insert before attrs:
|
||||||
or value.startswith(".. attribute::")
|
".. attribute::",
|
||||||
|
)
|
||||||
):
|
):
|
||||||
return idx
|
return idx
|
||||||
return len(lines) - 1
|
return len(lines) - 1
|
||||||
|
@ -411,7 +413,7 @@ class AdmonitionInserter:
|
||||||
# so its page needs no admonitions.
|
# so its page needs no admonitions.
|
||||||
continue
|
continue
|
||||||
|
|
||||||
attrs = sorted(attrs)
|
sorted_attrs = sorted(attrs)
|
||||||
|
|
||||||
# e.g. for admonition type "use_in" the title will be "Use in" and CSS class "use-in".
|
# e.g. for admonition type "use_in" the title will be "Use in" and CSS class "use-in".
|
||||||
admonition = f"""
|
admonition = f"""
|
||||||
|
@ -419,11 +421,11 @@ class AdmonitionInserter:
|
||||||
.. admonition:: {admonition_type.title().replace("_", " ")}
|
.. admonition:: {admonition_type.title().replace("_", " ")}
|
||||||
:class: {admonition_type.replace("_", "-")}
|
:class: {admonition_type.replace("_", "-")}
|
||||||
"""
|
"""
|
||||||
if len(attrs) > 1:
|
if len(sorted_attrs) > 1:
|
||||||
for target_attr in attrs:
|
for target_attr in sorted_attrs:
|
||||||
admonition += "\n * " + target_attr
|
admonition += "\n * " + target_attr
|
||||||
else:
|
else:
|
||||||
admonition += f"\n {attrs[0]}"
|
admonition += f"\n {sorted_attrs[0]}"
|
||||||
|
|
||||||
admonition += "\n " # otherwise an unexpected unindent warning will be issued
|
admonition += "\n " # otherwise an unexpected unindent warning will be issued
|
||||||
admonition_for_class[cls] = admonition
|
admonition_for_class[cls] = admonition
|
||||||
|
@ -516,12 +518,12 @@ class AdmonitionInserter:
|
||||||
# If it isn't resolved, we'll have the program throw an exception to be sure.
|
# If it isn't resolved, we'll have the program throw an exception to be sure.
|
||||||
try:
|
try:
|
||||||
cls = self._resolve_class(m.group("class_name"))
|
cls = self._resolve_class(m.group("class_name"))
|
||||||
except AttributeError:
|
except AttributeError as exc:
|
||||||
# skip known ForwardRef's that need not be resolved to a Telegram class
|
# skip known ForwardRef's that need not be resolved to a Telegram class
|
||||||
if self.FORWARD_REF_SKIP_PATTERN.match(str(arg)):
|
if self.FORWARD_REF_SKIP_PATTERN.match(str(arg)):
|
||||||
pass
|
pass
|
||||||
else:
|
else:
|
||||||
raise NotImplementedError(f"Could not process ForwardRef: {arg}")
|
raise NotImplementedError(f"Could not process ForwardRef: {arg}") from exc
|
||||||
else:
|
else:
|
||||||
yield cls
|
yield cls
|
||||||
|
|
||||||
|
@ -587,6 +589,7 @@ class AdmonitionInserter:
|
||||||
# If neither option works, this is not a PTB class.
|
# If neither option works, this is not a PTB class.
|
||||||
except (NameError, AttributeError):
|
except (NameError, AttributeError):
|
||||||
continue
|
continue
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
# You should have received a copy of the GNU Lesser Public License
|
# You should have received a copy of the GNU Lesser Public License
|
||||||
# along with this program. If not, see [http://www.gnu.org/licenses/].
|
# along with this program. If not, see [http://www.gnu.org/licenses/].
|
||||||
import inspect
|
import inspect
|
||||||
|
from typing import List
|
||||||
|
|
||||||
keyword_args = [
|
keyword_args = [
|
||||||
"Keyword Arguments:",
|
"Keyword Arguments:",
|
||||||
|
@ -84,12 +85,11 @@ get_updates_read_timeout_addition = [
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
def find_insert_pos_for_kwargs(lines: list[str]) -> int:
|
def find_insert_pos_for_kwargs(lines: List[str]) -> int:
|
||||||
"""Finds the correct position to insert the keyword arguments and returns the index."""
|
"""Finds the correct position to insert the keyword arguments and returns the index."""
|
||||||
for idx, value in reversed(list(enumerate(lines))): # reversed since :returns: is at the end
|
for idx, value in reversed(list(enumerate(lines))): # reversed since :returns: is at the end
|
||||||
if value.startswith("Returns"):
|
if value.startswith("Returns"):
|
||||||
return idx
|
return idx
|
||||||
else:
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -20,6 +20,8 @@ to link to the correct files & lines on github. Can be simplified once
|
||||||
https://github.com/sphinx-doc/sphinx/issues/1556 is closed
|
https://github.com/sphinx-doc/sphinx/issues/1556 is closed
|
||||||
"""
|
"""
|
||||||
import subprocess
|
import subprocess
|
||||||
|
from pathlib import Path
|
||||||
|
from typing import Dict, Tuple
|
||||||
|
|
||||||
from sphinx.util import logging
|
from sphinx.util import logging
|
||||||
|
|
||||||
|
@ -30,7 +32,7 @@ sphinx_logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
# must be a module-level variable so that it can be written to by the `autodoc-process-docstring`
|
# must be a module-level variable so that it can be written to by the `autodoc-process-docstring`
|
||||||
# event handler in `sphinx_hooks.py`
|
# event handler in `sphinx_hooks.py`
|
||||||
LINE_NUMBERS = {}
|
LINE_NUMBERS: Dict[str, Tuple[Path, int, int]] = {}
|
||||||
|
|
||||||
|
|
||||||
def _git_branch() -> str:
|
def _git_branch() -> str:
|
||||||
|
@ -52,7 +54,7 @@ git_branch = _git_branch()
|
||||||
base_url = "https://github.com/python-telegram-bot/python-telegram-bot/blob/"
|
base_url = "https://github.com/python-telegram-bot/python-telegram-bot/blob/"
|
||||||
|
|
||||||
|
|
||||||
def linkcode_resolve(_, info):
|
def linkcode_resolve(_, info) -> str:
|
||||||
"""See www.sphinx-doc.org/en/master/usage/extensions/linkcode.html"""
|
"""See www.sphinx-doc.org/en/master/usage/extensions/linkcode.html"""
|
||||||
combined = ".".join((info["module"], info["fullname"]))
|
combined = ".".join((info["module"], info["fullname"]))
|
||||||
# special casing for ExtBot which is due to the special structure of extbot.rst
|
# special casing for ExtBot which is due to the special structure of extbot.rst
|
||||||
|
@ -71,7 +73,7 @@ def linkcode_resolve(_, info):
|
||||||
line_info = LINE_NUMBERS.get(info["module"])
|
line_info = LINE_NUMBERS.get(info["module"])
|
||||||
|
|
||||||
if not line_info:
|
if not line_info:
|
||||||
return
|
return None
|
||||||
|
|
||||||
file, start_line, end_line = line_info
|
file, start_line, end_line = line_info
|
||||||
return f"{base_url}{git_branch}/{file}#L{start_line}-L{end_line}"
|
return f"{base_url}{git_branch}/{file}#L{start_line}-L{end_line}"
|
||||||
|
|
|
@ -67,9 +67,9 @@ def autodoc_skip_member(app, what, name, obj, skip, options):
|
||||||
return True
|
return True
|
||||||
break
|
break
|
||||||
|
|
||||||
if name == "filter" and obj.__module__ == "telegram.ext.filters":
|
if name == "filter" and obj.__module__ == "telegram.ext.filters" and not included_in_obj:
|
||||||
if not included_in_obj:
|
|
||||||
return True # return True to exclude from docs.
|
return True # return True to exclude from docs.
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
def autodoc_process_docstring(
|
def autodoc_process_docstring(
|
||||||
|
@ -118,7 +118,7 @@ def autodoc_process_docstring(
|
||||||
):
|
):
|
||||||
effective_insert: list[str] = media_write_timeout_deprecation
|
effective_insert: list[str] = media_write_timeout_deprecation
|
||||||
elif get_updates and to_insert.lstrip().startswith("read_timeout"):
|
elif get_updates and to_insert.lstrip().startswith("read_timeout"):
|
||||||
effective_insert = [to_insert] + get_updates_read_timeout_addition
|
effective_insert = [to_insert, *get_updates_read_timeout_addition]
|
||||||
else:
|
else:
|
||||||
effective_insert = [to_insert]
|
effective_insert = [to_insert]
|
||||||
|
|
||||||
|
@ -166,11 +166,11 @@ def autodoc_process_docstring(
|
||||||
autodoc_process_docstring(app, "method", f"{name}.__init__", obj.__init__, options, lines)
|
autodoc_process_docstring(app, "method", f"{name}.__init__", obj.__init__, options, lines)
|
||||||
|
|
||||||
|
|
||||||
def autodoc_process_bases(app, name, obj, option, bases: list):
|
def autodoc_process_bases(app, name, obj, option, bases: list) -> None:
|
||||||
"""Here we fine tune how the base class's classes are displayed."""
|
"""Here we fine tune how the base class's classes are displayed."""
|
||||||
for idx, base in enumerate(bases):
|
for idx, raw_base in enumerate(bases):
|
||||||
# let's use a string representation of the object
|
# let's use a string representation of the object
|
||||||
base = str(base)
|
base = str(raw_base)
|
||||||
|
|
||||||
# Special case for abstract context managers which are wrongly resoled for some reason
|
# Special case for abstract context managers which are wrongly resoled for some reason
|
||||||
if base.startswith("typing.AbstractAsyncContextManager"):
|
if base.startswith("typing.AbstractAsyncContextManager"):
|
||||||
|
|
|
@ -81,11 +81,12 @@ class TGConstXRefRole(PyXRefRole):
|
||||||
):
|
):
|
||||||
return repr(value), target
|
return repr(value), target
|
||||||
sphinx_logger.warning(
|
sphinx_logger.warning(
|
||||||
f"%s:%d: WARNING: Did not convert reference %s. :{CONSTANTS_ROLE}: is not supposed"
|
"%s:%d: WARNING: Did not convert reference %s. :%s: is not supposed"
|
||||||
" to be used with this type of target.",
|
" to be used with this type of target.",
|
||||||
refnode.source,
|
refnode.source,
|
||||||
refnode.line,
|
refnode.line,
|
||||||
refnode.rawsource,
|
refnode.rawsource,
|
||||||
|
CONSTANTS_ROLE,
|
||||||
)
|
)
|
||||||
return title, target
|
return title, target
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
import os
|
|
||||||
import re
|
import re
|
||||||
import sys
|
import sys
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
@ -8,7 +7,7 @@ from pathlib import Path
|
||||||
# documentation root, use os.path.abspath to make it absolute, like shown here.
|
# documentation root, use os.path.abspath to make it absolute, like shown here.
|
||||||
from sphinx.application import Sphinx
|
from sphinx.application import Sphinx
|
||||||
|
|
||||||
sys.path.insert(0, os.path.abspath("../.."))
|
sys.path.insert(0, str(Path("../..").resolve().absolute()))
|
||||||
|
|
||||||
# -- General configuration ------------------------------------------------
|
# -- General configuration ------------------------------------------------
|
||||||
# General information about the project.
|
# General information about the project.
|
||||||
|
@ -310,13 +309,13 @@ texinfo_documents = [
|
||||||
# Due to Sphinx behaviour, these imports only work when imported here, not at top of module.
|
# Due to Sphinx behaviour, these imports only work when imported here, not at top of module.
|
||||||
|
|
||||||
# Not used but must be imported for the linkcode extension to find it
|
# Not used but must be imported for the linkcode extension to find it
|
||||||
from docs.auxil.link_code import linkcode_resolve
|
from docs.auxil.link_code import linkcode_resolve # noqa: E402, F401
|
||||||
from docs.auxil.sphinx_hooks import (
|
from docs.auxil.sphinx_hooks import ( # noqa: E402
|
||||||
autodoc_process_bases,
|
autodoc_process_bases,
|
||||||
autodoc_process_docstring,
|
autodoc_process_docstring,
|
||||||
autodoc_skip_member,
|
autodoc_skip_member,
|
||||||
)
|
)
|
||||||
from docs.auxil.tg_const_role import CONSTANTS_ROLE, TGConstXRefRole
|
from docs.auxil.tg_const_role import CONSTANTS_ROLE, TGConstXRefRole # noqa: E402
|
||||||
|
|
||||||
|
|
||||||
def setup(app: Sphinx):
|
def setup(app: Sphinx):
|
||||||
|
|
|
@ -26,6 +26,7 @@ select = ["E", "F", "I", "PL", "UP", "RUF", "PTH", "C4", "B", "PIE", "SIM", "RET
|
||||||
[tool.ruff.lint.per-file-ignores]
|
[tool.ruff.lint.per-file-ignores]
|
||||||
"tests/*.py" = ["B018"]
|
"tests/*.py" = ["B018"]
|
||||||
"tests/**.py" = ["RUF012", "ASYNC101"]
|
"tests/**.py" = ["RUF012", "ASYNC101"]
|
||||||
|
"docs/**.py" = ["INP001"]
|
||||||
|
|
||||||
# PYLINT:
|
# PYLINT:
|
||||||
[tool.pylint."messages control"]
|
[tool.pylint."messages control"]
|
||||||
|
|
|
@ -5,4 +5,4 @@ license_files = LICENSE, LICENSE.dual, LICENSE.lesser
|
||||||
max-line-length = 99
|
max-line-length = 99
|
||||||
ignore = W503, W605
|
ignore = W503, W605
|
||||||
extend-ignore = E203, E704
|
extend-ignore = E203, E704
|
||||||
exclude = setup.py, setup-raw.py docs/source/conf.py
|
exclude = setup.py, setup_raw.py docs/source/conf.py
|
||||||
|
|
84
setup.py
84
setup.py
|
@ -4,15 +4,16 @@ import subprocess
|
||||||
import sys
|
import sys
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
from typing import Any, Dict, List, Tuple
|
||||||
|
|
||||||
from setuptools import find_packages, setup
|
from setuptools import find_packages, setup
|
||||||
|
|
||||||
|
|
||||||
def get_requirements():
|
def get_requirements() -> List[str]:
|
||||||
"""Build the requirements list for this project"""
|
"""Build the requirements list for this project"""
|
||||||
requirements_list = []
|
requirements_list = []
|
||||||
|
|
||||||
with Path("requirements.txt").open() as reqs:
|
with Path("requirements.txt").open(encoding="utf-8") as reqs:
|
||||||
for install in reqs:
|
for install in reqs:
|
||||||
if install.startswith("#"):
|
if install.startswith("#"):
|
||||||
continue
|
continue
|
||||||
|
@ -21,7 +22,7 @@ def get_requirements():
|
||||||
return requirements_list
|
return requirements_list
|
||||||
|
|
||||||
|
|
||||||
def get_packages_requirements(raw=False):
|
def get_packages_requirements(raw: bool = False) -> Tuple[List[str], List[str]]:
|
||||||
"""Build the package & requirements list for this project"""
|
"""Build the package & requirements list for this project"""
|
||||||
reqs = get_requirements()
|
reqs = get_requirements()
|
||||||
|
|
||||||
|
@ -34,68 +35,69 @@ def get_packages_requirements(raw=False):
|
||||||
return packs, reqs
|
return packs, reqs
|
||||||
|
|
||||||
|
|
||||||
def get_optional_requirements(raw=False):
|
def get_optional_requirements(raw: bool = False) -> Dict[str, List[str]]:
|
||||||
"""Build the optional dependencies"""
|
"""Build the optional dependencies"""
|
||||||
requirements = defaultdict(list)
|
requirements = defaultdict(list)
|
||||||
|
|
||||||
with Path("requirements-opts.txt").open() as reqs:
|
with Path("requirements-opts.txt").open(encoding="utf-8") as reqs:
|
||||||
for line in reqs:
|
for line in reqs:
|
||||||
line = line.strip()
|
effective_line = line.strip()
|
||||||
if not line or line.startswith("#"):
|
if not effective_line or effective_line.startswith("#"):
|
||||||
continue
|
continue
|
||||||
dependency, names = line.split("#")
|
dependency, names = effective_line.split("#")
|
||||||
dependency = dependency.strip()
|
dependency = dependency.strip()
|
||||||
for name in names.split(","):
|
for name in names.split(","):
|
||||||
name = name.strip()
|
effective_name = name.strip()
|
||||||
if name.endswith("!ext"):
|
if effective_name.endswith("!ext"):
|
||||||
if raw:
|
if raw:
|
||||||
continue
|
continue
|
||||||
else:
|
effective_name = effective_name[:-4]
|
||||||
name = name[:-4]
|
|
||||||
requirements["ext"].append(dependency)
|
requirements["ext"].append(dependency)
|
||||||
requirements[name].append(dependency)
|
requirements[effective_name].append(dependency)
|
||||||
requirements["all"].append(dependency)
|
requirements["all"].append(dependency)
|
||||||
|
|
||||||
return requirements
|
return requirements
|
||||||
|
|
||||||
|
|
||||||
def get_setup_kwargs(raw=False):
|
def get_setup_kwargs(raw: bool = False) -> Dict[str, Any]:
|
||||||
"""Builds a dictionary of kwargs for the setup function"""
|
"""Builds a dictionary of kwargs for the setup function"""
|
||||||
packages, requirements = get_packages_requirements(raw=raw)
|
packages, requirements = get_packages_requirements(raw=raw)
|
||||||
|
|
||||||
raw_ext = "-raw" if raw else ""
|
raw_ext = "-raw" if raw else ""
|
||||||
readme = Path(f'README{"_RAW" if raw else ""}.rst')
|
readme = Path(f'README{"_RAW" if raw else ""}.rst')
|
||||||
|
|
||||||
version_file = Path("telegram/_version.py").read_text()
|
version_file = Path("telegram/_version.py").read_text(encoding="utf-8")
|
||||||
first_part = version_file.split("# SETUP.PY MARKER")[0]
|
first_part = version_file.split("# SETUP.PY MARKER")[0]
|
||||||
exec(first_part)
|
exec(first_part) # pylint: disable=exec-used
|
||||||
|
|
||||||
kwargs = dict(
|
return {
|
||||||
script_name=f"setup{raw_ext}.py",
|
"script_name": f"setup{raw_ext}.py",
|
||||||
name=f"python-telegram-bot{raw_ext}",
|
"name": f"python-telegram-bot{raw_ext}",
|
||||||
version=locals()["__version__"],
|
"version": locals()["__version__"],
|
||||||
author="Leandro Toledo",
|
"author": "Leandro Toledo",
|
||||||
author_email="devs@python-telegram-bot.org",
|
"author_email": "devs@python-telegram-bot.org",
|
||||||
license="LGPLv3",
|
"license": "LGPLv3",
|
||||||
url="https://python-telegram-bot.org/",
|
"url": "https://python-telegram-bot.org/",
|
||||||
# Keywords supported by PyPI can be found at https://github.com/pypa/warehouse/blob/aafc5185e57e67d43487ce4faa95913dd4573e14/warehouse/templates/packaging/detail.html#L20-L58
|
# Keywords supported by PyPI can be found at
|
||||||
project_urls={
|
# https://github.com/pypa/warehouse/blob/aafc5185e57e67d43487ce4faa95913dd4573e14/
|
||||||
|
# warehouse/templates/packaging/detail.html#L20-L58
|
||||||
|
"project_urls": {
|
||||||
"Documentation": "https://docs.python-telegram-bot.org",
|
"Documentation": "https://docs.python-telegram-bot.org",
|
||||||
"Bug Tracker": "https://github.com/python-telegram-bot/python-telegram-bot/issues",
|
"Bug Tracker": "https://github.com/python-telegram-bot/python-telegram-bot/issues",
|
||||||
"Source Code": "https://github.com/python-telegram-bot/python-telegram-bot",
|
"Source Code": "https://github.com/python-telegram-bot/python-telegram-bot",
|
||||||
"News": "https://t.me/pythontelegrambotchannel",
|
"News": "https://t.me/pythontelegrambotchannel",
|
||||||
"Changelog": "https://docs.python-telegram-bot.org/en/stable/changelog.html",
|
"Changelog": "https://docs.python-telegram-bot.org/en/stable/changelog.html",
|
||||||
},
|
},
|
||||||
download_url=f"https://pypi.org/project/python-telegram-bot{raw_ext}/",
|
"download_url": f"https://pypi.org/project/python-telegram-bot{raw_ext}/",
|
||||||
keywords="python telegram bot api wrapper",
|
"keywords": "python telegram bot api wrapper",
|
||||||
description="We have made you a wrapper you can't refuse",
|
"description": "We have made you a wrapper you can't refuse",
|
||||||
long_description=readme.read_text(),
|
"long_description": readme.read_text(encoding="utf-8"),
|
||||||
long_description_content_type="text/x-rst",
|
"long_description_content_type": "text/x-rst",
|
||||||
packages=packages,
|
"packages": packages,
|
||||||
install_requires=requirements,
|
"install_requires": requirements,
|
||||||
extras_require=get_optional_requirements(raw=raw),
|
"extras_require": get_optional_requirements(raw=raw),
|
||||||
include_package_data=True,
|
"include_package_data": True,
|
||||||
classifiers=[
|
"classifiers": [
|
||||||
"Development Status :: 5 - Production/Stable",
|
"Development Status :: 5 - Production/Stable",
|
||||||
"Intended Audience :: Developers",
|
"Intended Audience :: Developers",
|
||||||
"License :: OSI Approved :: GNU Lesser General Public License v3 (LGPLv3)",
|
"License :: OSI Approved :: GNU Lesser General Public License v3 (LGPLv3)",
|
||||||
|
@ -111,16 +113,14 @@ def get_setup_kwargs(raw=False):
|
||||||
"Programming Language :: Python :: 3.11",
|
"Programming Language :: Python :: 3.11",
|
||||||
"Programming Language :: Python :: 3.12",
|
"Programming Language :: Python :: 3.12",
|
||||||
],
|
],
|
||||||
python_requires=">=3.8",
|
"python_requires": ">=3.8",
|
||||||
)
|
}
|
||||||
|
|
||||||
return kwargs
|
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main() -> None:
|
||||||
# If we're building, build ptb-raw as well
|
# If we're building, build ptb-raw as well
|
||||||
if set(sys.argv[1:]) in [{"bdist_wheel"}, {"sdist"}, {"sdist", "bdist_wheel"}]:
|
if set(sys.argv[1:]) in [{"bdist_wheel"}, {"sdist"}, {"sdist", "bdist_wheel"}]:
|
||||||
args = ["python", "setup-raw.py"]
|
args = ["python", "setup_raw.py"]
|
||||||
args.extend(sys.argv[1:])
|
args.extend(sys.argv[1:])
|
||||||
subprocess.run(args, check=True, capture_output=True)
|
subprocess.run(args, check=True, capture_output=True)
|
||||||
|
|
||||||
|
|
|
@ -60,7 +60,7 @@ class StringEnum(str, _enum.Enum):
|
||||||
|
|
||||||
|
|
||||||
# Apply the __repr__ modification and __str__ fix to IntEnum
|
# Apply the __repr__ modification and __str__ fix to IntEnum
|
||||||
class IntEnum(_enum.IntEnum): # pylint: disable=invalid-slots
|
class IntEnum(_enum.IntEnum):
|
||||||
"""Helper class for int enums where ``str(member)`` prints the value, but ``repr(member)``
|
"""Helper class for int enums where ``str(member)`` prints the value, but ``repr(member)``
|
||||||
gives ``EnumName.MEMBER_NAME``.
|
gives ``EnumName.MEMBER_NAME``.
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -29,7 +29,7 @@ those classes.
|
||||||
* Most of the constants in this module are grouped into enums.
|
* Most of the constants in this module are grouped into enums.
|
||||||
"""
|
"""
|
||||||
# TODO: Remove this when https://github.com/PyCQA/pylint/issues/6887 is resolved.
|
# TODO: Remove this when https://github.com/PyCQA/pylint/issues/6887 is resolved.
|
||||||
# pylint: disable=invalid-enum-extension,invalid-slots
|
# pylint: disable=invalid-enum-extension
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
"BOT_API_VERSION",
|
"BOT_API_VERSION",
|
||||||
|
|
|
@ -40,4 +40,4 @@ def test_build():
|
||||||
|
|
||||||
@skip_disabled
|
@skip_disabled
|
||||||
def test_build_raw():
|
def test_build_raw():
|
||||||
assert os.system("python setup-raw.py bdist_dumb") == 0 # pragma: no cover
|
assert os.system("python setup_raw.py bdist_dumb") == 0 # pragma: no cover
|
||||||
|
|
Loading…
Reference in a new issue