Compare commits
6 Commits
a6cdf03523
...
1626974759
Author | SHA1 | Date | |
---|---|---|---|
1626974759 | |||
ea63c67280 | |||
34c65f7ba5 | |||
dedff2aee5 | |||
cb69309307 | |||
0a50c26d1d |
1
.gitignore
vendored
1
.gitignore
vendored
@ -12,3 +12,4 @@ d2/book1/books
|
|||||||
*.tar.gz
|
*.tar.gz
|
||||||
.vscode/*
|
.vscode/*
|
||||||
!.vscode/launch.json
|
!.vscode/launch.json
|
||||||
|
python/build
|
||||||
|
@ -3,6 +3,7 @@ mypy_path =
|
|||||||
mypy-stubs,
|
mypy-stubs,
|
||||||
deps/com.github.aiortc.aiortc/src,
|
deps/com.github.aiortc.aiortc/src,
|
||||||
mypy-stubs/marisa-trie-types,
|
mypy-stubs/marisa-trie-types,
|
||||||
|
mypy-stubs/types-debugpy,
|
||||||
python
|
python
|
||||||
|
|
||||||
plugins =
|
plugins =
|
||||||
|
2
deps/com.github.aiortc.aiortc
vendored
2
deps/com.github.aiortc.aiortc
vendored
@ -1 +1 @@
|
|||||||
Subproject commit 1c8f054d29bfa9dae897f6101f95e8eb4f920305
|
Subproject commit 857be4a1fb1c07e2a239b61ce49b34cdd1698467
|
7
mypy-stubs/types-debugpy/debugpy/__init__.pyi
Normal file
7
mypy-stubs/types-debugpy/debugpy/__init__.pyi
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
def listen(
|
||||||
|
addr: tuple[str, int],
|
||||||
|
) -> None: ...
|
||||||
|
|
||||||
|
def wait_for_client() -> None: ...
|
||||||
|
|
||||||
|
def breakpoint() -> None: ...
|
@ -1392,6 +1392,12 @@ server {
|
|||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
class pass_ssh_osx_t:
|
||||||
|
class kwargs_t:
|
||||||
|
class Mode(enum.StrEnum):
|
||||||
|
clipboard = 'clipboard'
|
||||||
|
qrcode = 'qrcode'
|
||||||
|
|
||||||
def pass_ssh_osx(argv):
|
def pass_ssh_osx(argv):
|
||||||
assert isinstance(argv, list) and all([isinstance(o, str) for o in argv])
|
assert isinstance(argv, list) and all([isinstance(o, str) for o in argv])
|
||||||
parser = optparse.OptionParser()
|
parser = optparse.OptionParser()
|
||||||
@ -1415,6 +1421,17 @@ def pass_ssh_osx(argv):
|
|||||||
default=None,
|
default=None,
|
||||||
type=str,
|
type=str,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
parser.add_option(
|
||||||
|
'--mode',
|
||||||
|
dest='_mode',
|
||||||
|
choices=[
|
||||||
|
o.value
|
||||||
|
for o in pass_ssh_osx_t.kwargs_t.Mode
|
||||||
|
],
|
||||||
|
default=None,
|
||||||
|
help='a mode to retrieve the password',
|
||||||
|
)
|
||||||
parser.add_option(
|
parser.add_option(
|
||||||
'--debug',
|
'--debug',
|
||||||
dest='debug',
|
dest='debug',
|
||||||
@ -1424,6 +1441,11 @@ def pass_ssh_osx(argv):
|
|||||||
assert sys.platform in ['darwin', 'linux']
|
assert sys.platform in ['darwin', 'linux']
|
||||||
options, args = parser.parse_args(argv)
|
options, args = parser.parse_args(argv)
|
||||||
|
|
||||||
|
if options._mode is None:
|
||||||
|
options._mode = pass_ssh_osx_t.kwargs_t.Mode.clipboard.value
|
||||||
|
|
||||||
|
options.mode = pass_ssh_osx_t.kwargs_t.Mode(options._mode)
|
||||||
|
|
||||||
if options.clipboard_copy is None:
|
if options.clipboard_copy is None:
|
||||||
if sys.platform == 'linux':
|
if sys.platform == 'linux':
|
||||||
options.clipboard_copy = 'wl-copy'
|
options.clipboard_copy = 'wl-copy'
|
||||||
@ -1596,23 +1618,30 @@ def pass_ssh_osx(argv):
|
|||||||
assert not password is None
|
assert not password is None
|
||||||
|
|
||||||
|
|
||||||
try:
|
if options.mode is pass_ssh_osx_t.kwargs_t.Mode.clipboard:
|
||||||
clipboard_set(password)
|
try:
|
||||||
get_time = lambda : datetime.datetime.now().timestamp()
|
clipboard_set(password)
|
||||||
start = get_time()
|
get_time = lambda : datetime.datetime.now().timestamp()
|
||||||
while True:
|
start = get_time()
|
||||||
cur = get_time()
|
while True:
|
||||||
remains = 10 - (cur - start)
|
cur = get_time()
|
||||||
if remains <= 1e-8:
|
remains = 10 - (cur - start)
|
||||||
break
|
if remains <= 1e-8:
|
||||||
else:
|
break
|
||||||
print('\r%5.2fs remains' % remains, end='')
|
else:
|
||||||
time.sleep(0.1)
|
print('\r%5.2fs remains' % remains, end='')
|
||||||
except KeyboardInterrupt:
|
time.sleep(0.1)
|
||||||
pass
|
except KeyboardInterrupt:
|
||||||
|
pass
|
||||||
|
|
||||||
clipboard_set('')
|
clipboard_set('')
|
||||||
print('\rcleared cliboard\n', end='')
|
print('\rcleared cliboard\n', end='')
|
||||||
|
elif options.mode is pass_ssh_osx_t.kwargs_t.Mode.qrcode:
|
||||||
|
assert subprocess.run(r'''
|
||||||
|
qrencode -t PNG -o - | feh -
|
||||||
|
''', input=password.encode('utf-8'), shell=True).returncode == 0
|
||||||
|
else:
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
def vpn(argv: list[str]) -> None:
|
def vpn(argv: list[str]) -> None:
|
||||||
python_path : list[str]
|
python_path : list[str]
|
||||||
|
14
python/online/fxreader/pr34/commands_typed/asyncio.py
Normal file
14
python/online/fxreader/pr34/commands_typed/asyncio.py
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
import logging
|
||||||
|
import asyncio
|
||||||
|
|
||||||
|
from typing import (Any,)
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
def handle_task_result(fut: asyncio.Future[Any]) -> None:
|
||||||
|
try:
|
||||||
|
fut.result()
|
||||||
|
|
||||||
|
logger.debug(dict(fut=fut, msg='done'), stacklevel=2,)
|
||||||
|
except:
|
||||||
|
logger.exception('', stacklevel=2,)
|
284
python/online/fxreader/pr34/commands_typed/cli.py
Normal file
284
python/online/fxreader/pr34/commands_typed/cli.py
Normal file
@ -0,0 +1,284 @@
|
|||||||
|
import dataclasses
|
||||||
|
import io
|
||||||
|
import glob
|
||||||
|
import os
|
||||||
|
import pathlib
|
||||||
|
import logging
|
||||||
|
import sys
|
||||||
|
import subprocess
|
||||||
|
import shutil
|
||||||
|
import abc
|
||||||
|
|
||||||
|
from .os import shutil_which
|
||||||
|
|
||||||
|
from typing import (
|
||||||
|
Optional,
|
||||||
|
Literal,
|
||||||
|
Any,
|
||||||
|
)
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
@dataclasses.dataclass
|
||||||
|
class Project:
|
||||||
|
source_dir : pathlib.Path
|
||||||
|
build_dir : pathlib.Path
|
||||||
|
dest_dir : pathlib.Path
|
||||||
|
|
||||||
|
@dataclasses.dataclass
|
||||||
|
class Dependency:
|
||||||
|
name: str
|
||||||
|
mode : Literal['pyproject', 'meson', 'meson-python', 'm']
|
||||||
|
source_path : pathlib.Path
|
||||||
|
args: Optional[list[str]] = None
|
||||||
|
|
||||||
|
@dataclasses.dataclass
|
||||||
|
class DistSettings:
|
||||||
|
wheel_dir : pathlib.Path
|
||||||
|
python_path: pathlib.Path
|
||||||
|
env_path: pathlib.Path
|
||||||
|
|
||||||
|
class CLI(abc.ABC):
|
||||||
|
@property
|
||||||
|
@abc.abstractmethod
|
||||||
|
def dist_settings(self) -> DistSettings:
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
@property
|
||||||
|
@abc.abstractmethod
|
||||||
|
def projects(self) -> dict[str, Project]:
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
@property
|
||||||
|
@abc.abstractmethod
|
||||||
|
def dependencies(self) -> dict[str, Dependency]:
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
def deploy_fetch_dist(
|
||||||
|
self,
|
||||||
|
force: bool,
|
||||||
|
) -> None:
|
||||||
|
for k, o in self.dependencies.items():
|
||||||
|
whl_glob = self.dist_settings.wheel_dir / ('*%s*.whl' % o.name.replace('.', '_'))
|
||||||
|
if len(glob.glob(
|
||||||
|
str(whl_glob)
|
||||||
|
)) == 0 or force:
|
||||||
|
if o.source_path.exists():
|
||||||
|
def whl_files_get() -> list[dict[str, Any]]:
|
||||||
|
return [
|
||||||
|
dict(
|
||||||
|
path=o,
|
||||||
|
stat=os.stat(o).st_mtime,
|
||||||
|
)
|
||||||
|
for o in glob.glob(
|
||||||
|
str(whl_glob)
|
||||||
|
)
|
||||||
|
]
|
||||||
|
|
||||||
|
present_files = whl_files_get()
|
||||||
|
|
||||||
|
if o.mode == 'm':
|
||||||
|
if (o.source_path / 'm.py').exists():
|
||||||
|
cmd = [
|
||||||
|
sys.executable,
|
||||||
|
o.source_path / 'm.py',
|
||||||
|
'deploy:wheel',
|
||||||
|
'-o', self.settings.wheel_dir,
|
||||||
|
]
|
||||||
|
|
||||||
|
if not o.args is None:
|
||||||
|
cmd.extend(o.args)
|
||||||
|
|
||||||
|
subprocess.check_call(cmd)
|
||||||
|
else:
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
updated_files = whl_files_get()
|
||||||
|
|
||||||
|
def index_get(o: dict[str, Any]) -> tuple[Any, ...]:
|
||||||
|
return (o['path'], o['stat'])
|
||||||
|
|
||||||
|
present_files_index = {
|
||||||
|
index_get(o) : o
|
||||||
|
for o in present_files
|
||||||
|
}
|
||||||
|
|
||||||
|
new_files : list[dict[str, Any]] = []
|
||||||
|
|
||||||
|
for o in updated_files:
|
||||||
|
entry_index = index_get(o)
|
||||||
|
|
||||||
|
if not entry_index in present_files_index:
|
||||||
|
new_files.append(o)
|
||||||
|
|
||||||
|
if len(new_files) == 0:
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
latest_file = sorted(
|
||||||
|
new_files,
|
||||||
|
key=lambda x: x['stat']
|
||||||
|
)[-1]
|
||||||
|
|
||||||
|
subprocess.check_call([
|
||||||
|
self.dist_settings.python_path,
|
||||||
|
'-m', 'pip',
|
||||||
|
'install',
|
||||||
|
latest_file['path'],
|
||||||
|
])
|
||||||
|
|
||||||
|
def deploy_wheel(
|
||||||
|
self,
|
||||||
|
project_name: str,
|
||||||
|
argv: Optional[list[str]] = None,
|
||||||
|
output_dir: Optional[pathlib.Path] = None,
|
||||||
|
force: Optional[bool] = None,
|
||||||
|
env: Optional[dict[str, str]] = None,
|
||||||
|
) -> None:
|
||||||
|
project = self.projects[project_name]
|
||||||
|
|
||||||
|
# subprocess.check_call([
|
||||||
|
# sys.argv[0],
|
||||||
|
# # sys.executable,
|
||||||
|
# '-p', options.project,
|
||||||
|
# Command.meson_setup.value,
|
||||||
|
# ])
|
||||||
|
|
||||||
|
assert argv is None or len(argv) == 0
|
||||||
|
|
||||||
|
self.meson_install(
|
||||||
|
project_name=project_name,
|
||||||
|
force=force,
|
||||||
|
)
|
||||||
|
|
||||||
|
if env is None:
|
||||||
|
env = dict()
|
||||||
|
|
||||||
|
cmd = [
|
||||||
|
sys.executable,
|
||||||
|
'-m',
|
||||||
|
'build',
|
||||||
|
'-w', '-n',
|
||||||
|
'-Csetup-args=-Dmodes=pyproject',
|
||||||
|
'-Cbuild-dir=%s' % str(project.build_dir / 'pyproject'),
|
||||||
|
'-Csetup-args=-Dinstall_path=%s' % str(project.dest_dir),
|
||||||
|
# '-Cbuild-dir=%s' % str(project.build_dir),
|
||||||
|
str(project.source_dir),
|
||||||
|
]
|
||||||
|
|
||||||
|
if not output_dir is None:
|
||||||
|
cmd.extend(['-o', str(output_dir)])
|
||||||
|
|
||||||
|
logger.info(dict(env=env))
|
||||||
|
|
||||||
|
subprocess.check_call(
|
||||||
|
cmd,
|
||||||
|
env=dict(list(os.environ.items())) | env,
|
||||||
|
)
|
||||||
|
|
||||||
|
def meson_install(
|
||||||
|
self,
|
||||||
|
project_name: str,
|
||||||
|
force: Optional[bool] = None,
|
||||||
|
argv: Optional[list[str]] = None,
|
||||||
|
) -> None:
|
||||||
|
project = self.projects[project_name]
|
||||||
|
|
||||||
|
if force is None:
|
||||||
|
force = False
|
||||||
|
|
||||||
|
if argv is None:
|
||||||
|
argv = []
|
||||||
|
|
||||||
|
if force and project.dest_dir.exists():
|
||||||
|
shutil.rmtree(project.dest_dir)
|
||||||
|
|
||||||
|
subprocess.check_call([
|
||||||
|
shutil_which('meson', True,),
|
||||||
|
'install',
|
||||||
|
'-C',
|
||||||
|
project.build_dir / 'meson',
|
||||||
|
'--destdir', project.dest_dir,
|
||||||
|
*argv,
|
||||||
|
])
|
||||||
|
|
||||||
|
for o in glob.glob(
|
||||||
|
str(project.dest_dir / 'lib' / 'pkgconfig' / '*.pc'),
|
||||||
|
recursive=True,
|
||||||
|
):
|
||||||
|
logger.info(dict(
|
||||||
|
path=o,
|
||||||
|
action='patch prefix',
|
||||||
|
))
|
||||||
|
|
||||||
|
with io.open(o, 'r') as f:
|
||||||
|
content = f.read()
|
||||||
|
|
||||||
|
with io.open(o, 'w') as f:
|
||||||
|
f.write(
|
||||||
|
content.replace('prefix=/', 'prefix=${pcfiledir}/../../')
|
||||||
|
)
|
||||||
|
def ninja(
|
||||||
|
self,
|
||||||
|
project_name: str,
|
||||||
|
argv: Optional[list[str]] = None,
|
||||||
|
) -> None:
|
||||||
|
project = self.projects[project_name]
|
||||||
|
|
||||||
|
if argv is None:
|
||||||
|
argv = []
|
||||||
|
|
||||||
|
subprocess.check_call([
|
||||||
|
shutil_which('ninja', True),
|
||||||
|
'-C',
|
||||||
|
str(project.build_dir / 'meson'),
|
||||||
|
*argv,
|
||||||
|
])
|
||||||
|
|
||||||
|
def meson_compile(
|
||||||
|
self,
|
||||||
|
project_name: str,
|
||||||
|
argv: Optional[list[str]] = None,
|
||||||
|
) -> None:
|
||||||
|
project = self.projects[project_name]
|
||||||
|
|
||||||
|
if argv is None:
|
||||||
|
argv = []
|
||||||
|
|
||||||
|
subprocess.check_call([
|
||||||
|
shutil_which('meson', True,),
|
||||||
|
'compile',
|
||||||
|
'-C',
|
||||||
|
project.build_dir / 'meson',
|
||||||
|
*argv,
|
||||||
|
])
|
||||||
|
|
||||||
|
def meson_setup(
|
||||||
|
self,
|
||||||
|
project_name: str,
|
||||||
|
force: bool,
|
||||||
|
argv: Optional[list[str]] = None,
|
||||||
|
) -> None:
|
||||||
|
project = self.projects[project_name]
|
||||||
|
|
||||||
|
if argv is None:
|
||||||
|
argv = []
|
||||||
|
|
||||||
|
if force:
|
||||||
|
if (project.build_dir / 'meson').exists():
|
||||||
|
logger.info(dict(action='removing build dir', path=project.build_dir / 'meson'))
|
||||||
|
shutil.rmtree(project.build_dir / 'meson')
|
||||||
|
|
||||||
|
cmd = [
|
||||||
|
shutil_which('meson', True,),
|
||||||
|
'setup',
|
||||||
|
str(project.source_dir),
|
||||||
|
str(project.build_dir / 'meson'),
|
||||||
|
'-Dmodes=["meson"]',
|
||||||
|
# '-Dpkgconfig.relocatable=true',
|
||||||
|
'-Dprefix=/',
|
||||||
|
*argv,
|
||||||
|
]
|
||||||
|
|
||||||
|
logger.info(dict(cmd=cmd))
|
||||||
|
|
||||||
|
subprocess.check_call(cmd)
|
114
python/online/fxreader/pr34/commands_typed/cli_bootstrap.py
Normal file
114
python/online/fxreader/pr34/commands_typed/cli_bootstrap.py
Normal file
@ -0,0 +1,114 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
import glob
|
||||||
|
import dataclasses
|
||||||
|
import pathlib
|
||||||
|
import sys
|
||||||
|
import subprocess
|
||||||
|
import os
|
||||||
|
import logging
|
||||||
|
|
||||||
|
from typing import (Self, Optional,)
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
@dataclasses.dataclass
|
||||||
|
class BootstrapSettings:
|
||||||
|
env_path: pathlib.Path
|
||||||
|
python_path: pathlib.Path
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get(
|
||||||
|
cls,
|
||||||
|
base_dir: Optional[pathlib.Path] = None,
|
||||||
|
) -> Self:
|
||||||
|
if base_dir is None:
|
||||||
|
base_dir = pathlib.Path.cwd()
|
||||||
|
|
||||||
|
env_path = base_dir / '.venv'
|
||||||
|
python_path = env_path / 'bin' / 'python3'
|
||||||
|
|
||||||
|
return cls(
|
||||||
|
env_path=env_path,
|
||||||
|
python_path=python_path,
|
||||||
|
)
|
||||||
|
|
||||||
|
def env_bootstrap() -> None:
|
||||||
|
bootstrap_settings = BootstrapSettings.get()
|
||||||
|
|
||||||
|
subprocess.check_call([
|
||||||
|
'uv', 'venv', '--seed', '--offline',
|
||||||
|
str(bootstrap_settings.env_path)
|
||||||
|
])
|
||||||
|
|
||||||
|
subprocess.check_call([
|
||||||
|
'uv',
|
||||||
|
'pip',
|
||||||
|
'install',
|
||||||
|
'-p',
|
||||||
|
bootstrap_settings.python_path,
|
||||||
|
'--offline',
|
||||||
|
'uv',
|
||||||
|
])
|
||||||
|
|
||||||
|
subprocess.check_call([
|
||||||
|
bootstrap_settings.python_path,
|
||||||
|
'-m',
|
||||||
|
'uv', 'pip', 'install',
|
||||||
|
'--offline',
|
||||||
|
'build', 'setuptools', 'meson-python', 'pybind11',
|
||||||
|
])
|
||||||
|
|
||||||
|
early_wheels = glob.glob(
|
||||||
|
str(
|
||||||
|
pathlib.Path(__file__).parent / 'deps' / 'dist' / 'early' / '*.whl'
|
||||||
|
)
|
||||||
|
)
|
||||||
|
if len(early_wheels) > 0:
|
||||||
|
subprocess.check_call([
|
||||||
|
bootstrap_settings.python_path,
|
||||||
|
'-m',
|
||||||
|
'uv', 'pip', 'install',
|
||||||
|
'--offline',
|
||||||
|
*early_wheels,
|
||||||
|
])
|
||||||
|
|
||||||
|
def paths_equal(
|
||||||
|
a: pathlib.Path | str,
|
||||||
|
b: pathlib.Path | str
|
||||||
|
) -> bool:
|
||||||
|
return (
|
||||||
|
os.path.abspath(str(a)) ==
|
||||||
|
os.path.abspath(str(b))
|
||||||
|
)
|
||||||
|
|
||||||
|
def run() -> None:
|
||||||
|
bootstrap_settings = BootstrapSettings.get()
|
||||||
|
|
||||||
|
logging.basicConfig(level=logging.INFO)
|
||||||
|
|
||||||
|
if not bootstrap_settings.env_path.exists():
|
||||||
|
env_bootstrap()
|
||||||
|
|
||||||
|
logger.info([sys.executable, sys.argv, bootstrap_settings.python_path])
|
||||||
|
|
||||||
|
if not paths_equal(sys.executable, bootstrap_settings.python_path):
|
||||||
|
os.execv(
|
||||||
|
str(bootstrap_settings.python_path),
|
||||||
|
[
|
||||||
|
str(bootstrap_settings.python_path),
|
||||||
|
*sys.argv,
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
os.execv(
|
||||||
|
str(bootstrap_settings.python_path),
|
||||||
|
[
|
||||||
|
str(bootstrap_settings.python_path),
|
||||||
|
pathlib.Path(__file__).parent / 'cli.py',
|
||||||
|
*sys.argv[1:],
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
run()
|
25
python/online/fxreader/pr34/commands_typed/os.py
Normal file
25
python/online/fxreader/pr34/commands_typed/os.py
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
import shutil
|
||||||
|
|
||||||
|
from typing import (overload, Optional, Literal,)
|
||||||
|
|
||||||
|
@overload
|
||||||
|
def shutil_which(
|
||||||
|
name: str,
|
||||||
|
raise_on_failure: Literal[True],
|
||||||
|
) -> str: ...
|
||||||
|
|
||||||
|
@overload
|
||||||
|
def shutil_which(
|
||||||
|
name: str,
|
||||||
|
raise_on_failure: bool,
|
||||||
|
) -> Optional[str]: ...
|
||||||
|
|
||||||
|
def shutil_which(
|
||||||
|
name: str,
|
||||||
|
raise_on_failure: bool,
|
||||||
|
) -> Optional[str]:
|
||||||
|
res = shutil.which(name)
|
||||||
|
if res is None and raise_on_failure:
|
||||||
|
raise NotImplementedError
|
||||||
|
else:
|
||||||
|
return res
|
Loading…
Reference in New Issue
Block a user