Skip to content

Commit 910707a

Browse files
committed
Merge branch 'main' of github.com:waketzheng/carstino
2 parents badab3c + 7edeb5c commit 910707a

3 files changed

Lines changed: 144 additions & 14 deletions

File tree

pip_conf.py

Lines changed: 43 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,8 @@
3030
"""
3131

3232
__author__ = "waketzheng@gmail.com"
33-
__updated_at__ = "2025.11.22"
34-
__version__ = "0.8.7"
33+
__updated_at__ = "2025.11.26"
34+
__version__ = "0.8.9"
3535
import contextlib
3636
import functools
3737
import os
@@ -608,7 +608,7 @@ def check_installed(self):
608608

609609

610610
class UvMirror(Mirror):
611-
GITHUB_PROXY = "https://hub.gitmirror.com/"
611+
GITHUB_PROXY = "https://hk.gh-proxy.org/"
612612
PYTHON_DOWNLOAD_URL = (
613613
"https://github.com/astral-sh/python-build-standalone/releases/download"
614614
)
@@ -622,10 +622,39 @@ def allow_insecure(url):
622622
return ""
623623
return '\nallow-insecure-host=["{}"]'.format(parse_host(url))
624624

625+
@classmethod
626+
def _get_python_mirror(cls, default):
627+
# type: (str) -> str
628+
choices = {"default": default}
629+
for name in ("PIP_CONF_PYTHON_MIRROR", "UV_PYTHON_INSTALL_MIRROR"):
630+
value = os.getenv(name)
631+
if value and value not in choices.values():
632+
choices[name] = value
633+
if len(choices) == 1:
634+
return default
635+
tip = "Which url do you want to set for python install mirror? (Leave blank to use default)"
636+
items = list(choices.items())
637+
choices_text = "\n".join(
638+
[
639+
"{i}. {key} ({value})".format(i=i, key=key, value=value)
640+
for i, (key, value) in enumerate(items)
641+
]
642+
)
643+
a = input(tip + "\n" + choices_text + "\n").strip()
644+
if a:
645+
try:
646+
url = items[int(a)][1]
647+
except (TypeError, ValueError, IndexError):
648+
print("Invalid choice, default will be used.")
649+
else:
650+
return url
651+
return default
652+
625653
@classmethod
626654
def python_install_mirror(cls):
627655
# type: () -> str
628-
return cls.TEMPLATE.format(cls.GITHUB_PROXY + cls.PYTHON_DOWNLOAD_URL)
656+
default = cls.GITHUB_PROXY + cls.PYTHON_DOWNLOAD_URL
657+
return cls.TEMPLATE.format(cls._get_python_mirror(default))
629658

630659
def build_content(self, url=None, extra_index=None):
631660
# type: (Optional[str], Optional[str]) -> str
@@ -660,8 +689,8 @@ def set(self, set_python_mirror=False):
660689
config_toml_path = os.path.join(dirpath, filename)
661690
text = self.build_content()
662691
if os.path.exists(config_toml_path):
663-
with open(config_toml_path) as f:
664-
content = f.read().strip()
692+
with open(config_toml_path, "rb") as f:
693+
content = f.read().strip().decode("utf-8")
665694
if text in content:
666695
print("uv mirror set as expected. Skip!")
667696
return None
@@ -752,8 +781,8 @@ def fix_v1_6_error(cls):
752781
print("WARNING: plugin file not found {}".format(file))
753782
return
754783
s = "semver"
755-
with open(file) as f:
756-
text = f.read()
784+
with open(file, "rb") as f:
785+
text = f.read().decode("utf-8")
757786
if s in text:
758787
text = text.replace(s, "constraints")
759788
with open(file, "w") as f:
@@ -838,8 +867,8 @@ def set(self):
838867
item = "[plugins.pypi_mirror]"
839868
text = item + '{}url = "{}"'.format(os.linesep, self.url)
840869
if os.path.exists(config_toml_path):
841-
with open(config_toml_path) as f:
842-
content = f.read().strip()
870+
with open(config_toml_path, "rb") as f:
871+
content = f.read().strip().decode("utf-8")
843872
if text in content:
844873
print("poetry mirror set as expected. Skip!")
845874
return None
@@ -904,8 +933,8 @@ def init_pip_conf(
904933
text = TEMPLATE.format(url, parse_host(url))
905934
conf_file = get_conf_path(is_windows, at_etc)
906935
if os.path.exists(conf_file):
907-
with open(conf_file) as fp:
908-
s = fp.read()
936+
with open(conf_file, "rb") as fp:
937+
s = fp.read().decode("utf-8")
909938
if text in s:
910939
print("Pip source already be configured as expected.\nSkip!")
911940
return None
@@ -936,8 +965,8 @@ def can_set_global():
936965

937966
def read_lines(filename):
938967
# type: (str) -> list[str]
939-
with open(filename) as f:
940-
s = f.read()
968+
with open(filename, "rb") as f:
969+
s = f.read().decode("utf-8")
941970
return s.splitlines()
942971

943972

tests/test_box.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
from waketbox import DigitGame
2+
3+
4+
def test_box():
5+
game = DigitGame("helloworld")
6+
raw = "32roqworisdonasdfjqiqirqieru938345ev !@#$%^&***()_+\n\t\r\n"
7+
pad = game.black(raw)
8+
assert len(pad) <= len(raw) * 4 + game.token_length
9+
assert game.white(pad) == raw

waketbox.py

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
#!/usr/bin/env python
2+
"""
3+
Waket Box
4+
5+
Usage::
6+
FIX_ZWJ=xxx python <me>.py <path-to-file>.txt
7+
8+
"""
9+
10+
from __future__ import annotations
11+
12+
import base64
13+
import os
14+
import random
15+
import string
16+
import sys
17+
import zlib
18+
from functools import cached_property
19+
from pathlib import Path
20+
21+
22+
class DigitGame:
23+
def __init__(self, token: str) -> None:
24+
self._token = token
25+
26+
@property
27+
def token(self) -> str:
28+
return self._token
29+
30+
@cached_property
31+
def token_length(self) -> int:
32+
return len(self.token)
33+
34+
def is_padded(self, text: str) -> bool:
35+
if len(text) % self.token_length != 1:
36+
return False
37+
return text[-1] in string.ascii_lowercase[: self.token_length]
38+
39+
def black(self, raw: str) -> str:
40+
b1 = zlib.compress(raw.encode())
41+
b2 = base64.urlsafe_b64encode(b1)
42+
s1 = b2.strip(b"=").hex()
43+
len1 = len(s1)
44+
multi = int(len1 // self.token_length) + bool(len1 % self.token_length)
45+
tokens = self.token * multi
46+
delta = len(tokens) - len1
47+
last_char = string.ascii_lowercase[delta]
48+
if delta:
49+
choices = string.digits + "abcdef"
50+
s1 += "".join(random.choice(choices) for _ in range(delta))
51+
chars = [chr(int(a, 16) ^ ord(b)) for a, b in zip(s1, tokens, strict=True)]
52+
return "".join(chars) + last_char
53+
54+
def white(self, pad: str) -> str:
55+
last_char = pad[-1]
56+
delta = string.ascii_lowercase.index(last_char)
57+
pad = pad[:-1]
58+
tokens = self.token * (len(pad) // self.token_length)
59+
unpad = [ord(p) ^ ord(t) for p, t in zip(pad, tokens, strict=True)]
60+
if delta:
61+
unpad = unpad[:-delta]
62+
s1 = "".join(hex(i)[2:] for i in unpad)
63+
b2 = bytes.fromhex(s1)
64+
len1 = len(b2)
65+
mod = len1 % 4
66+
if mod:
67+
b2 += b"=" * (4 - mod)
68+
b1 = base64.urlsafe_b64decode(b2)
69+
return zlib.decompress(b1).decode()
70+
71+
72+
def main():
73+
if len(sys.argv) < 2:
74+
print(__doc__.replace("<me>", Path(__file__).stem))
75+
return
76+
file = Path(sys.argv[1])
77+
text = file.read_text()
78+
box = DigitGame(os.environ["FIX_ZWJ"])
79+
if "--pad" not in sys.argv and box.is_padded(text):
80+
raw = box.white(text)
81+
new_file = file.with_name(file.name + ".raw")
82+
new_file.write_text(raw)
83+
print(f"Save raw to {new_file}")
84+
else:
85+
padded = box.black(text)
86+
new_file = file.with_name(file.name + ".pad")
87+
new_file.write_text(padded)
88+
print(f"Create pad to {new_file}")
89+
90+
91+
if __name__ == "__main__":
92+
main()

0 commit comments

Comments
 (0)