Skip to content

Commit ccee618

Browse files
authored
fix: 🐛 Support Windows (#31)
1 parent ffc1083 commit ccee618

File tree

7 files changed

+53
-36
lines changed

7 files changed

+53
-36
lines changed

.github/workflows/main.yml

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,17 +16,16 @@ concurrency:
1616

1717
jobs:
1818
build:
19-
20-
runs-on: ubuntu-20.04
2119
strategy:
2220
fail-fast: false
2321
matrix:
22+
os: [ubuntu-20.04, macos-12, windows-2022]
2423
python-version: ["3.6", "3.7", "3.8", "3.9", "3.10"]
25-
24+
runs-on: ${{ matrix.os }}
2625
steps:
27-
- uses: actions/checkout@v3
26+
- uses: actions/checkout@v4
2827
- name: Set up Python ${{ matrix.python-version }}
29-
uses: actions/setup-python@v3
28+
uses: actions/setup-python@v5
3029
with:
3130
python-version: ${{ matrix.python-version }}
3231
- name: Install dependencies

pyencrypt/cli.py

Lines changed: 10 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,7 @@
1010

1111
from pyencrypt import __description__, __version__
1212
from pyencrypt.decrypt import decrypt_file
13-
from pyencrypt.encrypt import (can_encrypt, encrypt_file, encrypt_key,
14-
generate_so_file)
13+
from pyencrypt.encrypt import (can_encrypt, encrypt_file, encrypt_key, generate_so_file)
1514
from pyencrypt.generate import generate_aes_key
1615
from pyencrypt.license import MAX_DATETIME, MIN_DATETIME, generate_license_file
1716

@@ -34,13 +33,7 @@
3433
"""
3534

3635
PYTHON_MAJOR, PYTHON_MINOR = sys.version_info[:2]
37-
LAODER_FILE_NAME = click.style(
38-
"encrypted/loader.cpython-{major}{minor}{abi}-{platform}.so".format(
39-
major=PYTHON_MAJOR, minor=PYTHON_MINOR, abi=sys.abiflags, platform=sys.platform
40-
),
41-
blink=True,
42-
fg='blue'
43-
)
36+
LOADER_FILE_NAME = click.style("encrypted/{}", blink=True, fg='blue')
4437
LICENSE_FILE_NAME = click.style("license.lic", blink=True, fg='blue')
4538

4639
SUCCESS_ANSI = click.style('successfully', fg='green')
@@ -55,17 +48,17 @@
5548

5649
FINISH_ENCRYPT_MSG = f"""
5750
Encryption completed {SUCCESS_ANSI}.
58-
Please copy {LAODER_FILE_NAME} into your encrypted directory.
51+
Please copy {LOADER_FILE_NAME} into your encrypted directory.
5952
And then remove `encrypted` directory.
6053
Finally, add `import loader` at the top of your entry file.\
6154
"""
6255

6356
FINISH_DECRYPT_MSG = f"""
64-
Decryption completed {SUCCESS_ANSI}. Your origin source code has be put: %s
57+
Decryption completed {SUCCESS_ANSI}. Your origin source code has be put: {{work_dir}}
6558
"""
6659

6760
FINISH_GENERATE_LOADER_MSG = f"""
68-
Generate loader file {SUCCESS_ANSI}. Your loader file is located in {LAODER_FILE_NAME}
61+
Generate loader file {SUCCESS_ANSI}. Your loader file is located in {LOADER_FILE_NAME}
6962
"""
7063

7164
FINISH_GENERATE_LICENSE_MSG = f"""
@@ -187,11 +180,11 @@ def encrypt_command(ctx, pathname, replace, key, with_license, mac, ipv4, before
187180
raise Exception(f'{path} is not a valid path.')
188181

189182
cipher_key, d, n = encrypt_key(key.encode()) # 需要放进导入器中
190-
generate_so_file(cipher_key, d, n, license=with_license)
183+
loader_extension = generate_so_file(cipher_key, d, n, license=with_license)
191184
if with_license is True:
192185
generate_license_file(key, Path(os.getcwd()), after, before, mac, ipv4)
193186
click.echo(FINISH_GENERATE_LICENSE_MSG)
194-
click.echo(FINISH_ENCRYPT_MSG)
187+
click.echo(FINISH_ENCRYPT_MSG.format(loader_extension.name))
195188

196189

197190
@cli.command(name='decrypt')
@@ -227,7 +220,7 @@ def decrypt_command(ctx, pathname, replace, key):
227220
else:
228221
raise Exception(f'{path} is not a valid path.')
229222

230-
click.echo(FINISH_DECRYPT_MSG % work_dir)
223+
click.echo(FINISH_DECRYPT_MSG.format(work_dir=work_dir))
231224

232225

233226
@cli.command(name='generate')
@@ -237,8 +230,8 @@ def decrypt_command(ctx, pathname, replace, key):
237230
def generate_loader(ctx, key):
238231
"""Generate loader file using specified key"""
239232
cipher_key, d, n = encrypt_key(key.encode())
240-
generate_so_file(cipher_key, d, n, Path(os.getcwd()))
241-
click.echo(FINISH_GENERATE_LOADER_MSG)
233+
loader_extension = generate_so_file(cipher_key, d, n, Path(os.getcwd()))
234+
click.echo(FINISH_GENERATE_LOADER_MSG.format(loader_extension.name))
242235

243236

244237
@cli.command(name='license')

pyencrypt/encrypt.py

Lines changed: 26 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import os
2+
import sys
23
import re
34
from pathlib import Path
45
from typing import Optional
@@ -57,13 +58,14 @@ def generate_so_file(cipher_key: str, d: int, n: int, base_dir: Optional[Path] =
5758
need_import_files = ['ntt.py', 'aes.py', 'decrypt.py', 'license.py']
5859
for file in need_import_files:
5960
file_path = path / file
60-
decrypt_source_ls.append(REMOVE_SELF_IMPORT.sub('', file_path.read_text()))
61+
decrypt_source_ls.append(REMOVE_SELF_IMPORT.sub('', file_path.read_text(encoding="utf-8")))
6162

6263
loader_source_path = path / 'loader.py'
63-
loader_source = REMOVE_SELF_IMPORT.sub('', loader_source_path.read_text()).replace(
64-
"__private_key = ''", f"__private_key = '{private_key}'", 1
65-
).replace("__cipher_key = ''", f"__cipher_key = '{cipher_key}'", 1).replace(
66-
'license = None', f'license = {license}', 1
64+
loader_source = (
65+
REMOVE_SELF_IMPORT.sub("", loader_source_path.read_text(encoding="utf-8"))
66+
.replace("__private_key = ''", f"__private_key = '{private_key}'", 1)
67+
.replace("__cipher_key = ''", f"__cipher_key = '{cipher_key}'", 1)
68+
.replace("license = None", f"license = {license}", 1)
6769
)
6870

6971
if base_dir is None:
@@ -79,9 +81,14 @@ def generate_so_file(cipher_key: str, d: int, n: int, base_dir: Optional[Path] =
7981
# Origin file
8082
loader_origin_file_path = temp_dir / 'loader_origin.py'
8183
loader_origin_file_path.touch(exist_ok=True)
82-
loader_origin_file_path.write_text(f"{decrypt_source}\n{loader_source}")
84+
loader_origin_file_path.write_text(
85+
f"{decrypt_source}\n{loader_source}", encoding="utf-8"
86+
)
8387

84-
loader_file_path.write_text(python_minifier.minify(loader_origin_file_path.read_text()))
88+
loader_file_path.write_text(
89+
python_minifier.minify(loader_origin_file_path.read_text(encoding="utf-8")),
90+
encoding="utf-8",
91+
)
8592

8693
from setuptools import setup # isort:skip
8794
from Cython.Build import cythonize
@@ -91,7 +98,18 @@ def generate_so_file(cipher_key: str, d: int, n: int, base_dir: Optional[Path] =
9198
script_args=['build_ext', '--build-lib', temp_dir.as_posix()],
9299
cmdclass={'build_ext': build_ext},
93100
)
94-
return list(temp_dir.glob('loader.cpython-*-*.so'))[0].absolute()
101+
if sys.platform.startswith('win'):
102+
# loader.cp36-win_amd64.pyd
103+
pattern = 'loader.cp*-*.pyd'
104+
else:
105+
# loader.cpython-36m-x86_64-linux-gnu.so
106+
# loader.cpython-36m-darwin.so
107+
pattern = "loader.cpython-*-*.so"
108+
109+
loader_extension = next(temp_dir.glob(pattern), None)
110+
if loader_extension is None:
111+
raise Exception(f"Can't find loader extension in {temp_dir.as_posix()}")
112+
return loader_extension.absolute()
95113

96114

97115
def encrypt_file(path: Path, key: str, delete_origin: bool = False, new_path: Optional[Path] = None):

pyencrypt/license.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212

1313
DATE_FORMAT = '%Y-%m-%dT%H:%M:%S%z'
1414
MIN_DATETIME = datetime.now().astimezone()
15-
MAX_DATETIME = datetime.max.replace(year=9998).astimezone()
15+
MAX_DATETIME = datetime(year=2999, month=12, day=31).astimezone()
1616

1717

1818
def get_mac_address() -> str:

tests/conftest.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ def file_and_loader(request, tmp_path_factory):
3030
file_path.write_text("""\
3131
def {function_name}():
3232
{code}
33-
""".format(function_name=function_name, code=code))
33+
""".format(function_name=function_name, code=code), encoding='utf-8')
3434
# generate loader.so
3535
key = generate_aes_key()
3636
new_path = file_path.with_suffix('.pye')
@@ -74,7 +74,7 @@ def package_and_loader(request, tmp_path_factory):
7474
file_path.write_text("""\
7575
def {function_name}():
7676
{code}
77-
""".format(function_name=function_name, code=code))
77+
""".format(function_name=function_name, code=code), encoding='utf-8')
7878

7979
new_path = file_path.with_suffix('.pye')
8080
key = generate_aes_key()

tests/test_encrypt.py

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import os
22
from pathlib import Path
33
import shutil
4+
import sys
45

56
import pytest
67
from pyencrypt.encrypt import can_encrypt, encrypt_file, encrypt_key, generate_so_file
@@ -47,7 +48,10 @@ def test_generate_so_file(self, key, tmp_path):
4748
assert generate_so_file(cipher_key, d, n, tmp_path)
4849
assert (tmp_path / 'encrypted' / 'loader.py').exists() is True
4950
assert (tmp_path / 'encrypted' / 'loader_origin.py').exists() is True
50-
assert list((tmp_path / 'encrypted').glob('loader.cpython-*-*.so')) != []
51+
if sys.platform.startswith('win'):
52+
assert next((tmp_path / 'encrypted').glob('loader.cp*-*.pyd'), None) is not None
53+
else:
54+
assert next((tmp_path / 'encrypted').glob('loader.cpython-*-*.so'), None) is not None
5155

5256
@pytest.mark.parametrize('key', [
5357
AES_KEY,
@@ -58,7 +62,10 @@ def test_generate_so_file_default_path(self, key):
5862
assert generate_so_file(cipher_key, d, n)
5963
assert (Path(os.getcwd()) / 'encrypted' / 'loader.py').exists() is True
6064
assert (Path(os.getcwd()) / 'encrypted' / 'loader_origin.py').exists() is True
61-
assert list((Path(os.getcwd()) / 'encrypted').glob('loader.cpython-*-*.so')) != []
65+
if sys.platform.startswith('win'):
66+
assert next((Path(os.getcwd()) / 'encrypted').glob('loader.cp*-*.pyd'), None) is not None
67+
else:
68+
assert next((Path(os.getcwd()) / 'encrypted').glob('loader.cpython-*-*.so'), None) is not None
6269

6370

6471
@pytest.mark.parametrize(

tests/test_license.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ def test_check_license_invalid(self, key, tmp_path):
8181
license_file_path = generate_license_file(key, path=tmp_path)
8282
license_data = json.loads(license_file_path.read_text())
8383
license_data['signature'] = 'invalid'
84-
license_file_path.write_text(json.dumps(license_data))
84+
license_file_path.write_text(json.dumps(license_data), encoding='utf-8')
8585
with pytest.raises(Exception) as excinfo:
8686
check_license(license_file_path, key)
8787
assert str(excinfo.value) == 'License signature is invalid.'

0 commit comments

Comments
 (0)