Skip to content

Commit a34f0b9

Browse files
authored
Make cryptography Dependency Optional & Refactor Some Tests (python-telegram-bot#2386)
* Make cryptography optional * Try fixing CI * Try some more * Update pytest, mypy & pyupgrade, refactor test_meta, hope that things start to work * Fix filterwarnings * Mama mia! Here we go again! * Add stupid debug prints * A new hope
1 parent eee8921 commit a34f0b9

17 files changed

Lines changed: 196 additions & 51 deletions

.github/workflows/test.yml

Lines changed: 13 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,6 @@ jobs:
1515
matrix:
1616
python-version: [3.6, 3.7, 3.8, 3.9]
1717
os: [ubuntu-latest, windows-latest, macos-latest]
18-
include:
19-
- os: ubuntu-latest
20-
python-version: 3.7
21-
test-build: True
22-
- os: windows-latest
23-
python-version: 3.7
24-
test-build: True
2518
fail-fast: False
2619
steps:
2720
- uses: actions/checkout@v2
@@ -40,18 +33,24 @@ jobs:
4033
python -W ignore -m pip install -r requirements-dev.txt
4134
4235
- name: Test with pytest
36+
# We run three different suites here
37+
# 1. Test just the build process
38+
# 2. Test just test_no_passport.py without passport dependencies being installed
39+
# 3. Test everything else
40+
# The second one is achieved by mocking the imports, see test_no_passport.py for details
4341
run: |
44-
pytest -v -m nocoverage
45-
nocov_exit=$?
46-
pytest -v -m "not nocoverage" --cov
47-
cov_exit=$?
48-
global_exit=$(( nocov_exit > cov_exit ? nocov_exit : cov_exit ))
42+
pytest -v -s --cov -k test_no_passport.py
43+
no_passport_exit=$?
44+
export TEST_NO_PASSPORT='false'
45+
pytest -v -s --cov --cov-append
46+
passport_exit=$?
47+
global_exit=$(( passport_exit > no_passport_exit ? passport_exit : no_passport_exit ))
4948
exit ${global_exit}
5049
env:
5150
JOB_INDEX: ${{ strategy.job-index }}
5251
BOTS: W3sidG9rZW4iOiAiNjk2MTg4NzMyOkFBR1Z3RUtmSEhsTmpzY3hFRE5LQXdraEdzdFpfa28xbUMwIiwgInBheW1lbnRfcHJvdmlkZXJfdG9rZW4iOiAiMjg0Njg1MDYzOlRFU1Q6WldGaU1UUmxNbVF5TnpNeSIsICJib3RfbmFtZSI6ICJQVEIgdGVzdHMgb24gVHJhdmlzIHVzaW5nIENQeXRob24gMi43IiwgInN1cGVyX2dyb3VwX2lkIjogIi0xMDAxMzkwOTgzOTk3IiwgImJvdF91c2VybmFtZSI6ICJAcHRiX3RyYXZpc19jcHl0aG9uXzI3X2JvdCJ9LCB7InRva2VuIjogIjY3MTQ2ODg4NjpBQUdQR2ZjaVJJQlVORmU4MjR1SVZkcTdKZTNfWW5BVE5HdyIsICJwYXltZW50X3Byb3ZpZGVyX3Rva2VuIjogIjI4NDY4NTA2MzpURVNUOlpHWXdPVGxrTXpNeE4yWTIiLCAiYm90X25hbWUiOiAiUFRCIHRlc3RzIG9uIFRyYXZpcyB1c2luZyBDUHl0aG9uIDMuNCIsICJzdXBlcl9ncm91cF9pZCI6ICItMTAwMTQ0NjAyMjUyMiIsICJib3RfdXNlcm5hbWUiOiAiQHB0Yl90cmF2aXNfY3B5dGhvbl8zNF9ib3QifSwgeyJ0b2tlbiI6ICI2MjkzMjY1Mzg6QUFGUnJaSnJCN29CM211ekdzR0pYVXZHRTVDUXpNNUNVNG8iLCAicGF5bWVudF9wcm92aWRlcl90b2tlbiI6ICIyODQ2ODUwNjM6VEVTVDpNbU01WVdKaFl6a3hNMlUxIiwgImJvdF9uYW1lIjogIlBUQiB0ZXN0cyBvbiBUcmF2aXMgdXNpbmcgQ1B5dGhvbiAzLjUiLCAic3VwZXJfZ3JvdXBfaWQiOiAiLTEwMDE0OTY5MTc3NTAiLCAiYm90X3VzZXJuYW1lIjogIkBwdGJfdHJhdmlzX2NweXRob25fMzVfYm90In0sIHsidG9rZW4iOiAiNjQwMjA4OTQzOkFBRmhCalFwOXFtM1JUeFN6VXBZekJRakNsZS1Kano1aGNrIiwgInBheW1lbnRfcHJvdmlkZXJfdG9rZW4iOiAiMjg0Njg1MDYzOlRFU1Q6WXpoa1pUZzFOamMxWXpWbCIsICJib3RfbmFtZSI6ICJQVEIgdGVzdHMgb24gVHJhdmlzIHVzaW5nIENQeXRob24gMy42IiwgInN1cGVyX2dyb3VwX2lkIjogIi0xMDAxMzMzODcxNDYxIiwgImJvdF91c2VybmFtZSI6ICJAcHRiX3RyYXZpc19jcHl0aG9uXzM2X2JvdCJ9LCB7InRva2VuIjogIjY5NTEwNDA4ODpBQUhmenlsSU9qU0lJUy1lT25JMjB5MkUyMEhvZEhzZnotMCIsICJwYXltZW50X3Byb3ZpZGVyX3Rva2VuIjogIjI4NDY4NTA2MzpURVNUOk9HUTFNRGd3WmpJd1pqRmwiLCAiYm90X25hbWUiOiAiUFRCIHRlc3RzIG9uIFRyYXZpcyB1c2luZyBDUHl0aG9uIDMuNyIsICJzdXBlcl9ncm91cF9pZCI6ICItMTAwMTQ3ODI5MzcxNCIsICJib3RfdXNlcm5hbWUiOiAiQHB0Yl90cmF2aXNfY3B5dGhvbl8zN19ib3QifSwgeyJ0b2tlbiI6ICI2OTE0MjM1NTQ6QUFGOFdrakNaYm5IcVBfaTZHaFRZaXJGRWxackdhWU9oWDAiLCAicGF5bWVudF9wcm92aWRlcl90b2tlbiI6ICIyODQ2ODUwNjM6VEVTVDpZamM1TlRoaU1tUXlNV1ZoIiwgImJvdF9uYW1lIjogIlBUQiB0ZXN0cyBvbiBUcmF2aXMgdXNpbmcgUHlQeSAyLjciLCAic3VwZXJfZ3JvdXBfaWQiOiAiLTEwMDEzNjM5MzI1NzMiLCAiYm90X3VzZXJuYW1lIjogIkBwdGJfdHJhdmlzX3B5cHlfMjdfYm90In0sIHsidG9rZW4iOiAiNjg0MzM5OTg0OkFBRk1nRUVqcDAxcjVyQjAwN3lDZFZOc2c4QWxOc2FVLWNjIiwgInBheW1lbnRfcHJvdmlkZXJfdG9rZW4iOiAiMjg0Njg1MDYzOlRFU1Q6TVRBek1UWTNNR1V5TmpnMCIsICJib3RfbmFtZSI6ICJQVEIgdGVzdHMgb24gVHJhdmlzIHVzaW5nIFB5UHkgMy41IiwgInN1cGVyX2dyb3VwX2lkIjogIi0xMDAxNDA3ODM2NjA1IiwgImJvdF91c2VybmFtZSI6ICJAcHRiX3RyYXZpc19weXB5XzM1X2JvdCJ9LCB7InRva2VuIjogIjY5MDA5MTM0NzpBQUZMbVI1cEFCNVljcGVfbU9oN3pNNEpGQk9oMHozVDBUbyIsICJwYXltZW50X3Byb3ZpZGVyX3Rva2VuIjogIjI4NDY4NTA2MzpURVNUOlpEaGxOekU1TURrd1lXSmkiLCAiYm90X25hbWUiOiAiUFRCIHRlc3RzIG9uIEFwcFZleW9yIHVzaW5nIENQeXRob24gMy40IiwgInN1cGVyX2dyb3VwX2lkIjogIi0xMDAxMjc5NjAwMDI2IiwgImJvdF91c2VybmFtZSI6ICJAcHRiX2FwcHZleW9yX2NweXRob25fMzRfYm90In0sIHsidG9rZW4iOiAiNjk0MzA4MDUyOkFBRUIyX3NvbkNrNTVMWTlCRzlBTy1IOGp4aVBTNTVvb0JBIiwgInBheW1lbnRfcHJvdmlkZXJfdG9rZW4iOiAiMjg0Njg1MDYzOlRFU1Q6WW1aaVlXWm1NakpoWkdNeSIsICJib3RfbmFtZSI6ICJQVEIgdGVzdHMgb24gQXBwVmV5b3IgdXNpbmcgQ1B5dGhvbiAyLjciLCAic3VwZXJfZ3JvdXBfaWQiOiAiLTEwMDEyOTMwNzkxNjUiLCAiYm90X3VzZXJuYW1lIjogIkBwdGJfYXBwdmV5b3JfY3B5dGhvbl8yN19ib3QifSwgeyJ0b2tlbiI6ICIxMDU1Mzk3NDcxOkFBRzE4bkJfUzJXQXd1SjNnN29oS0JWZ1hYY2VNbklPeVNjIiwgInBheW1lbnRfcHJvdmlkZXJfdG9rZW4iOiAiMjg0Njg1MDYzOlRFU1Q6TmpBd056QXpZalZpTkdOayIsICJuYW1lIjogIlBUQiB0ZXN0cyBbMF0iLCAic3VwZXJfZ3JvdXBfaWQiOiAiLTEwMDExODU1MDk2MzYiLCAidXNlcm5hbWUiOiAicHRiXzBfYm90In0sIHsidG9rZW4iOiAiMTA0NzMyNjc3MTpBQUY4bk90ODFGcFg4bGJidno4VWV3UVF2UmZUYkZmQnZ1SSIsICJwYXltZW50X3Byb3ZpZGVyX3Rva2VuIjogIjI4NDY4NTA2MzpURVNUOllUVTFOVEk0WkdSallqbGkiLCAibmFtZSI6ICJQVEIgdGVzdHMgWzFdIiwgInN1cGVyX2dyb3VwX2lkIjogIi0xMDAxNDg0Nzk3NjEyIiwgInVzZXJuYW1lIjogInB0Yl8xX2JvdCJ9LCB7InRva2VuIjogIjk3MTk5Mjc0NTpBQUdPa09hVzBOSGpnSXY1LTlqUWJPajR2R3FkaFNGLVV1cyIsICJwYXltZW50X3Byb3ZpZGVyX3Rva2VuIjogIjI4NDY4NTA2MzpURVNUOk5XWmtNV1ZoWWpsallqVTUiLCAibmFtZSI6ICJQVEIgdGVzdHMgWzJdIiwgInN1cGVyX2dyb3VwX2lkIjogIi0xMDAxNDAyMjU1MDcwIiwgInVzZXJuYW1lIjogInB0Yl8yX2JvdCJ9XQ==
53-
TEST_BUILD: ${{ matrix.test-build }}
54-
TEST_PRE_COMMIT: ${{ matrix.test-pre-commit }}
52+
TEST_BUILD: "true"
53+
TEST_NO_PASSPORT: "true"
5554
shell: bash --noprofile --norc {0}
5655

5756
- name: Submit coverage

.pre-commit-config.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,12 +21,12 @@ repos:
2121
args:
2222
- --rcfile=setup.cfg
2323
- repo: https://github.com/pre-commit/mirrors-mypy
24-
rev: v0.790
24+
rev: v0.800
2525
hooks:
2626
- id: mypy
2727
files: ^(telegram|examples)/.*\.py$
2828
- repo: https://github.com/asottile/pyupgrade
29-
rev: v2.7.4
29+
rev: v2.10.0
3030
hooks:
3131
- id: pyupgrade
3232
files: ^(telegram|examples|tests)/.*\.py$

README.rst

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,16 @@ In case you have a previously cloned local repository already, you should initia
137137
138138
$ git submodule update --init --recursive
139139
140+
---------------------
141+
Optional Dependencies
142+
---------------------
143+
144+
PTB can be installed with optional dependencies:
145+
146+
* ``pip install python-telegram-bot[passport]`` installs the `cryptography <https://cryptography.io>`_ library. Use this, if you want to use Telegram Passport related functionality.
147+
* ``pip install python-telegram-bot[ujson]`` installs the `ujson <https://pypi.org/project/ujson/>`_ library. It will then be used for JSON de- & encoding, which can bring speed up compared to the standard `json <https://docs.python.org/3/library/json.html>`_ library.
148+
* ``pip install python-telegram-bot[socks]`` installs the `PySocks <https://pypi.org/project/PySocks/>`_ library. Use this, if you want to work behind a Socks5 server.
149+
140150
===============
141151
Getting started
142152
===============

README_RAW.rst

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,15 @@ Note
137137

138138
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`.
139139

140+
---------------------
141+
Optional Dependencies
142+
---------------------
143+
144+
PTB can be installed with optional dependencies:
145+
146+
* ``pip install python-telegram-bot-raw[passport]`` installs the `cryptography <https://cryptography.io>`_ library. Use this, if you want to use Telegram Passport related functionality.
147+
* ``pip install python-telegram-bot-raw[ujson]`` installs the `ujson <https://pypi.org/project/ujson/>`_ library. It will then be used for JSON de- & encoding, which can bring speed up compared to the standard `json <https://docs.python.org/3/library/json.html>`_ library.
148+
140149
===============
141150
Getting started
142151
===============

requirements-dev.txt

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,15 @@
1+
# cryptography is an optional dependency, but running the tests properly requires it
2+
cryptography!=3.4,!=3.4.1,!=3.4.2,!=3.4.3
3+
14
pre-commit
25
# Make sure that the versions specified here match the pre-commit settings
36
black==20.8b1
47
flake8==3.8.4
58
pylint==2.6.0
6-
mypy==0.790
7-
pyupgrade==2.7.4
9+
mypy==0.800
10+
pyupgrade==2.10.0
811

9-
pytest==4.2.0
10-
# Need older attrs version for pytest 4.2.0
11-
attrs==19.1.0
12+
pytest==6.2.2
1213

1314
flaky
1415
beautifulsoup4

requirements.txt

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
certifi
2-
cryptography!=3.4
32
# only telegram.ext: # Keep this line here; used in setup(-raw).py
43
tornado>=5.1
54
APScheduler==3.6.3

setup.cfg

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,9 @@ addopts = --no-success-flaky-report -rsxX
2727
filterwarnings =
2828
error
2929
ignore::DeprecationWarning
30-
ignore::telegram.utils.deprecate.TelegramDeprecationWarning
30+
; Unfortunately due to https://github.com/pytest-dev/pytest/issues/8343 we can't have this here
31+
; and instead do a trick directly in tests/conftest.py
32+
; ignore::telegram.utils.deprecate.TelegramDeprecationWarning
3133

3234
[coverage:run]
3335
branch = True

setup.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,9 @@ def get_setup_kwargs(raw=False):
8484
install_requires=requirements,
8585
extras_require={
8686
'json': 'ujson',
87-
'socks': 'PySocks'
87+
'socks': 'PySocks',
88+
# 3.4-3.4.3 contained some cyclical import bugs
89+
'passport': 'cryptography!=3.4,!=3.4.1,!=3.4.2,!=3.4.3',
8890
},
8991
include_package_data=True,
9092
classifiers=[

telegram/bot.py

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,15 @@
4141
except ImportError:
4242
import json # type: ignore[no-redef] # noqa: F723
4343

44-
from cryptography.hazmat.backends import default_backend
45-
from cryptography.hazmat.primitives import serialization
44+
try:
45+
from cryptography.hazmat.backends import default_backend
46+
from cryptography.hazmat.primitives import serialization
47+
48+
CRYPTO_INSTALLED = True
49+
except ImportError:
50+
default_backend = None # type: ignore[assignment]
51+
serialization = None # type: ignore[assignment]
52+
CRYPTO_INSTALLED = False
4653

4754
from telegram import (
4855
Animation,
@@ -212,6 +219,11 @@ def __init__(
212219
self.logger = logging.getLogger(__name__)
213220

214221
if private_key:
222+
if not CRYPTO_INSTALLED:
223+
raise RuntimeError(
224+
'To use Telegram Passports, PTB must be installed via `pip install '
225+
'python-telegram-bot[passport]`.'
226+
)
215227
self.private_key = serialization.load_pem_private_key(
216228
private_key, password=private_key_password, backend=default_backend()
217229
)

telegram/passport/credentials.py

Lines changed: 25 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -25,12 +25,21 @@
2525
from base64 import b64decode
2626
from typing import TYPE_CHECKING, Any, List, Optional, Tuple, Union, no_type_check
2727

28-
from cryptography.hazmat.backends import default_backend
29-
from cryptography.hazmat.primitives.asymmetric.padding import MGF1, OAEP
30-
from cryptography.hazmat.primitives.ciphers import Cipher
31-
from cryptography.hazmat.primitives.ciphers.algorithms import AES
32-
from cryptography.hazmat.primitives.ciphers.modes import CBC
33-
from cryptography.hazmat.primitives.hashes import SHA1, SHA256, SHA512, Hash
28+
try:
29+
from cryptography.hazmat.backends import default_backend
30+
from cryptography.hazmat.primitives.asymmetric.padding import MGF1, OAEP
31+
from cryptography.hazmat.primitives.ciphers import Cipher
32+
from cryptography.hazmat.primitives.ciphers.algorithms import AES
33+
from cryptography.hazmat.primitives.ciphers.modes import CBC
34+
from cryptography.hazmat.primitives.hashes import SHA1, SHA256, SHA512, Hash
35+
36+
CRYPTO_INSTALLED = True
37+
except ImportError:
38+
default_backend = None
39+
MGF1, OAEP, Cipher, AES, CBC = (None, None, None, None, None) # type: ignore[misc]
40+
SHA1, SHA256, SHA512, Hash = (None, None, None, None) # type: ignore[misc]
41+
42+
CRYPTO_INSTALLED = False
3443

3544
from telegram import TelegramError, TelegramObject
3645
from telegram.utils.types import JSONDict
@@ -74,6 +83,11 @@ def decrypt(secret, hash, data):
7483
:obj:`bytes`: The decrypted data as bytes.
7584
7685
"""
86+
if not CRYPTO_INSTALLED:
87+
raise RuntimeError(
88+
'To use Telegram Passports, PTB must be installed via `pip install '
89+
'python-telegram-bot[passport]`.'
90+
)
7791
# Make a SHA512 hash of secret + update
7892
digest = Hash(SHA512(), backend=default_backend())
7993
digest.update(secret + hash)
@@ -153,6 +167,11 @@ def decrypted_secret(self) -> str:
153167
private/public key but can also suggest malformed/tampered data.
154168
"""
155169
if self._decrypted_secret is None:
170+
if not CRYPTO_INSTALLED:
171+
raise RuntimeError(
172+
'To use Telegram Passports, PTB must be installed via `pip install '
173+
'python-telegram-bot[passport]`.'
174+
)
156175
# Try decrypting according to step 1 at
157176
# https://core.telegram.org/passport#decrypting-data
158177
# We make sure to base64 decode the secret first.

0 commit comments

Comments
 (0)