Skip to content

Commit 6236654

Browse files
committed
refactor: remove Python 2 support, require Python 3.8+
- Removed all Python 2 compatibility code from py3compat.py - Removed long() type conversions from number.py (Python 3 int handles large numbers) - Added typer dependency for CLI functionality - Updated setup.py and pyproject.toml with typer dependency - Added console_scripts entry point for libcrypto CLI - All 117 tests passing - CLI functionality verified (generate, mnemonic commands working)
1 parent f8ecec0 commit 6236654

File tree

6 files changed

+82
-145
lines changed

6 files changed

+82
-145
lines changed

.github/workflows/test-and-build.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -174,4 +174,4 @@ jobs:
174174
175175
- name: Test key generation
176176
run: |
177-
python -c "from libcrypto import PrivateKey; pk = PrivateKey(1); print('Public key: ' + pk.get_public_key().hex[:32] + '...'); print('Key generation works')"
177+
python -c "from libcrypto import PrivateKey; pk = PrivateKey(1); print('Public key: ' + pk.get_public_key().hex[:32] + '...'); print('Key generation works')"

pyproject.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ classifiers = [
3939
]
4040
dependencies = [
4141
"rich",
42+
"typer",
4243
"wheel",
4344
"setuptools",
4445
]

setup.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ def get_long_description():
4141
python_requires=">=3.8",
4242
install_requires=[
4343
"rich>=14.0.0",
44+
"typer>=0.9.0",
4445
"wheel>=0.45.1",
4546
"setuptools>=80.9.0",
4647
],
@@ -101,7 +102,7 @@ def get_long_description():
101102
zip_safe=False,
102103
entry_points={
103104
"console_scripts": [
104-
# Add any command-line tools here if needed
105+
"libcrypto=libcrypto.cli:app",
105106
],
106107
},
107108
)

src/libcrypto/addresses.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,7 @@ def _get_uncompressed_pubkey(cls, public_key: bytes) -> bytes:
146146
@classmethod
147147
def _generate_ethereum_address(cls, public_key: bytes) -> str:
148148
"""Generate Ethereum address (with EIP-55 checksum)."""
149+
149150
uncompressed_key = cls._get_uncompressed_pubkey(public_key)
150151

151152
keccak_hash = keccak256(uncompressed_key)

src/libcrypto/cryptod/Crypto/Util/number.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -279,8 +279,8 @@ def getStrongPrime(N, e=0, false_positive_prob=1e-6, randfunc=None):
279279
# Use the accelerator if available
280280
if _fastmath is not None:
281281
return _fastmath.getStrongPrime(
282-
long(N), # noqa: F821 - long exists in Python 2
283-
long(e), # noqa: F821 - long exists in Python 2
282+
N,
283+
e,
284284
false_positive_prob,
285285
randfunc,
286286
)
@@ -400,7 +400,7 @@ def isPrime(N, false_positive_prob=1e-6, randfunc=None):
400400

401401
if _fastmath is not None:
402402
return _fastmath.isPrime(
403-
long(N), # noqa: F821 - long exists in Python 2
403+
N,
404404
false_positive_prob,
405405
randfunc,
406406
)
Lines changed: 74 additions & 140 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
# -*- coding: utf-8 -*-
22
#
3-
# Util/py3compat.py : Compatibility code for handling Py3k / Python 2.x
4-
#
5-
# Written in 2010 by Thorsten Behrens
3+
# Util/py3compat.py : Python 3 compatibility helpers
64
#
75
# ===================================================================
86
# The contents of this file are dedicated to the public domain. To
@@ -22,169 +20,109 @@
2220
# SOFTWARE.
2321
# ===================================================================
2422

25-
"""Compatibility code for handling string/bytes changes from Python 2.x to Py3k
26-
27-
In Python 2.x, strings (of type ''str'') contain binary data, including encoded
28-
Unicode text (e.g. UTF-8). The separate type ''unicode'' holds Unicode text.
29-
Unicode literals are specified via the u'...' prefix. Indexing or slicing
30-
either type always produces a string of the same type as the original.
31-
Data read from a file is always of '''str'' type.
32-
33-
In Python 3.x, strings (type ''str'') may only contain Unicode text. The u'...'
34-
prefix and the ''unicode'' type are now redundant. A new type (called
35-
''bytes'') has to be used for binary data (including any particular
36-
''encoding'' of a string). The b'...' prefix allows one to specify a binary
37-
literal. Indexing or slicing a string produces another string. Slicing a byte
38-
string produces another byte string, but the indexing operation produces an
39-
integer. Data read from a file is of '''str'' type if the file was opened in
40-
text mode, or of ''bytes'' type otherwise.
41-
42-
Since PyCrypto aims at supporting both Python 2.x and 3.x, the following helper
43-
functions are used to keep the rest of the library as independent as possible
44-
from the actual Python version.
45-
46-
In general, the code should always deal with binary strings, and use integers
47-
instead of 1-byte character strings.
48-
49-
b(s)
50-
Take a text string literal (with no prefix or with u'...' prefix) and
51-
make a byte string.
52-
bchr(c)
53-
Take an integer and make a 1-character byte string.
54-
bord(c)
55-
Take the result of indexing on a byte string and make an integer.
56-
tobytes(s)
57-
Take a text string, a byte string, or a sequence of character taken from
58-
a byte string and make a byte string.
23+
"""Python 3 compatibility helpers for bytes/string handling.
24+
25+
This module provides helper functions for consistent handling of bytes and strings.
26+
Requires Python 3.8+.
5927
"""
6028

6129
import sys
62-
import abc
63-
64-
65-
if sys.version_info[0] == 2:
66-
67-
def b(s):
68-
return s
69-
70-
def bchr(s):
71-
return chr(s)
72-
73-
def bstr(s):
74-
return str(s)
75-
76-
def bord(s):
77-
return ord(s)
78-
79-
def tobytes(s, encoding="latin-1"):
80-
if isinstance(s, unicode): # noqa: F821 - unicode exists in Python 2
81-
return s.encode(encoding)
82-
elif isinstance(s, str):
83-
return s
84-
elif isinstance(s, bytearray):
85-
return bytes(s)
86-
elif isinstance(s, memoryview):
87-
return s.tobytes()
88-
else:
89-
return "".join(s)
30+
from abc import ABC
31+
from io import BytesIO, StringIO
32+
from sys import maxsize as maxint
9033

91-
def tostr(bs):
92-
return bs
9334

94-
def byte_string(s):
95-
return isinstance(s, str)
35+
def b(s):
36+
"""Convert a text string to bytes."""
37+
return s.encode("latin-1")
9638

97-
# In Python 2, a memoryview does not support concatenation
98-
def concat_buffers(a, b):
99-
if isinstance(a, memoryview):
100-
a = a.tobytes()
101-
if isinstance(b, memoryview):
102-
b = b.tobytes()
103-
return a + b
10439

105-
from StringIO import StringIO
40+
def bchr(s):
41+
"""Convert an integer to a single-byte bytes object."""
42+
return bytes([s])
10643

107-
BytesIO = StringIO
10844

109-
from sys import maxint
45+
def bstr(s):
46+
"""Convert string or int to bytes."""
47+
if isinstance(s, str):
48+
return bytes(s, "latin-1")
49+
else:
50+
return bytes(s)
11051

111-
iter_range = xrange # noqa: F821 - xrange exists in Python 2
11252

113-
def is_native_int(x):
114-
return isinstance(x, (int, long)) # noqa: F821 - long exists in Python 2
53+
def bord(s):
54+
"""Return the integer value of a byte (identity function in Python 3)."""
55+
return s
11556

116-
def is_string(x):
117-
return isinstance(x, basestring) # noqa: F821 - basestring exists in Python 2
11857

119-
def is_bytes(x):
120-
return (
121-
isinstance(x, str) or isinstance(x, bytearray) or isinstance(x, memoryview)
122-
)
58+
def tobytes(s, encoding="latin-1"):
59+
"""Convert various types to bytes."""
60+
if isinstance(s, bytes):
61+
return s
62+
elif isinstance(s, bytearray):
63+
return bytes(s)
64+
elif isinstance(s, str):
65+
return s.encode(encoding)
66+
elif isinstance(s, memoryview):
67+
return s.tobytes()
68+
else:
69+
return bytes([s])
12370

124-
ABC = abc.ABCMeta("ABC", (object,), {"__slots__": ()})
12571

126-
FileNotFoundError = IOError
72+
def tostr(bs):
73+
"""Convert bytes to string."""
74+
return bs.decode("latin-1")
12775

128-
else:
12976

130-
def b(s):
131-
return s.encode("latin-1") # utf-8 would cause some side-effects we don't want
77+
def byte_string(s):
78+
"""Check if s is a bytes object."""
79+
return isinstance(s, bytes)
13280

133-
def bchr(s):
134-
return bytes([s])
13581

136-
def bstr(s):
137-
if isinstance(s, str):
138-
return bytes(s, "latin-1")
139-
else:
140-
return bytes(s)
82+
def concat_buffers(a, b):
83+
"""Concatenate two buffer-like objects."""
84+
return a + b
14185

142-
def bord(s):
143-
return s
144-
145-
def tobytes(s, encoding="latin-1"):
146-
if isinstance(s, bytes):
147-
return s
148-
elif isinstance(s, bytearray):
149-
return bytes(s)
150-
elif isinstance(s, str):
151-
return s.encode(encoding)
152-
elif isinstance(s, memoryview):
153-
return s.tobytes()
154-
else:
155-
return bytes([s])
15686

157-
def tostr(bs):
158-
return bs.decode("latin-1")
87+
iter_range = range
15988

160-
def byte_string(s):
161-
return isinstance(s, bytes)
16289

163-
def concat_buffers(a, b):
164-
return a + b
90+
def is_native_int(x):
91+
"""Check if x is a native integer."""
92+
return isinstance(x, int)
16593

166-
from io import BytesIO
167-
from io import StringIO
168-
from sys import maxsize as maxint
16994

170-
iter_range = range
95+
def is_string(x):
96+
"""Check if x is a string."""
97+
return isinstance(x, str)
17198

172-
def is_native_int(x):
173-
return isinstance(x, int)
17499

175-
def is_string(x):
176-
return isinstance(x, str)
100+
def is_bytes(x):
101+
"""Check if x is bytes-like."""
102+
return isinstance(x, (bytes, bytearray, memoryview))
177103

178-
def is_bytes(x):
179-
return (
180-
isinstance(x, bytes)
181-
or isinstance(x, bytearray)
182-
or isinstance(x, memoryview)
183-
)
184104

185-
from abc import ABC
105+
FileNotFoundError = FileNotFoundError
186106

187-
FileNotFoundError = FileNotFoundError
107+
__all__ = [
108+
"b",
109+
"bchr",
110+
"bstr",
111+
"bord",
112+
"tobytes",
113+
"tostr",
114+
"byte_string",
115+
"concat_buffers",
116+
"BytesIO",
117+
"StringIO",
118+
"maxint",
119+
"iter_range",
120+
"is_native_int",
121+
"is_string",
122+
"is_bytes",
123+
"ABC",
124+
"FileNotFoundError",
125+
]
188126

189127

190128
def _copy_bytes(start, end, seq):
@@ -197,7 +135,3 @@ def _copy_bytes(start, end, seq):
197135
return bytes(seq[start:end])
198136
else:
199137
return seq[start:end]
200-
201-
202-
del sys
203-
del abc

0 commit comments

Comments
 (0)