juc500 정상동작

This commit is contained in:
Macbook
2026-07-21 21:04:25 +09:00
parent a423db0556
commit bb9874b931
2917 changed files with 708025 additions and 668 deletions
@@ -0,0 +1,14 @@
"""Errors."""
from __future__ import annotations
class ProcessCallFailedError(RuntimeError):
"""Failed a process call."""
def __init__(self, code: int, out: str, err: str, cmd: list[str]) -> None:
super().__init__(code, out, err, cmd)
self.code = code
self.out = out
self.err = err
self.cmd = cmd
@@ -0,0 +1,193 @@
"""holds locking functionality that works across processes."""
from __future__ import annotations
import logging
import os
from abc import ABC, abstractmethod
from contextlib import contextmanager, suppress
from pathlib import Path
from threading import Lock, RLock
from typing import TYPE_CHECKING
from filelock import FileLock, Timeout
if TYPE_CHECKING:
from collections.abc import Iterator
from types import TracebackType
LOGGER = logging.getLogger(__name__)
class _CountedFileLock(FileLock):
def __init__(self, lock_file: str) -> None:
parent = os.path.dirname(lock_file)
with suppress(OSError):
os.makedirs(parent, exist_ok=True)
super().__init__(lock_file)
self.count = 0
self.thread_safe = RLock()
def acquire( # ty: ignore[invalid-method-override]
self,
timeout: float | None = None,
poll_interval: float = 0.05,
) -> None:
if not self.thread_safe.acquire(timeout=-1 if timeout is None else timeout):
raise Timeout(self.lock_file)
if self.count == 0:
try:
super().acquire(timeout, poll_interval)
except BaseException:
self.thread_safe.release()
raise
self.count += 1
def release(self, force: bool = False) -> None: # noqa: FBT002
with self.thread_safe:
if self.count > 0:
if self.count == 1:
super().release(force=force)
self.count -= 1
if self.count == 0:
# if we have no more users of this lock, release the thread lock
self.thread_safe.release()
_lock_store = {}
_store_lock = Lock()
class PathLockBase(ABC):
def __init__(self, folder: str | Path) -> None:
path = Path(folder)
self.path = path.resolve() if path.exists() else path
def __repr__(self) -> str:
return f"{self.__class__.__name__}({self.path})"
def __truediv__(self, other: str) -> PathLockBase:
return type(self)(self.path / other)
@abstractmethod
def __enter__(self) -> None:
raise NotImplementedError
@abstractmethod
def __exit__(
self, exc_type: type[BaseException] | None, exc_val: BaseException | None, exc_tb: TracebackType | None
) -> None:
raise NotImplementedError
@abstractmethod
@contextmanager
def lock_for_key(self, name: str, no_block: bool = False) -> Iterator[None]: # noqa: FBT002
raise NotImplementedError
@abstractmethod
@contextmanager
def non_reentrant_lock_for_key(self, name: str) -> Iterator[None]:
raise NotImplementedError
class ReentrantFileLock(PathLockBase):
def __init__(self, folder: str | Path) -> None:
super().__init__(folder)
self._lock = None
def _create_lock(self, name: str = "") -> _CountedFileLock:
lock_file = str(self.path / f"{name}.lock")
with _store_lock:
if lock_file not in _lock_store:
_lock_store[lock_file] = _CountedFileLock(lock_file)
return _lock_store[lock_file]
@staticmethod
def _del_lock(lock: _CountedFileLock | None) -> None:
if lock is not None:
with _store_lock, lock.thread_safe:
if lock.count == 0:
_lock_store.pop(lock.lock_file, None)
def __del__(self) -> None:
self._del_lock(self._lock)
def __enter__(self) -> None:
self._lock = self._create_lock()
self._lock_file(self._lock)
def __exit__(
self, exc_type: type[BaseException] | None, exc_val: BaseException | None, exc_tb: TracebackType | None
) -> None:
self._release(self._lock) # ty: ignore[invalid-argument-type]
self._del_lock(self._lock)
self._lock = None
def _lock_file(self, lock: _CountedFileLock, no_block: bool = False) -> None: # noqa: FBT002
# multiple processes might be trying to get a first lock... so we cannot check if this directory exist without
# a lock, but that lock might then become expensive, and it's not clear where that lock should live.
# Instead here we just ignore if we fail to create the directory.
with suppress(OSError):
os.makedirs(str(self.path), exist_ok=True)
try:
lock.acquire(0.0001)
except Timeout:
if no_block:
raise
LOGGER.debug("lock file %s present, will block until released", lock.lock_file)
lock.release() # release the acquire try from above
lock.acquire()
@staticmethod
def _release(lock: _CountedFileLock) -> None:
lock.release()
@contextmanager
def lock_for_key(self, name: str, no_block: bool = False) -> Iterator[None]: # noqa: FBT002
lock = self._create_lock(name)
try:
with self._lock_and_yield(lock, no_block):
yield
finally:
self._del_lock(lock)
lock = None
@contextmanager
def _lock_and_yield(self, lock: _CountedFileLock, no_block: bool) -> Iterator[None]:
self._lock_file(lock, no_block)
try:
yield
finally:
self._release(lock)
@contextmanager
def non_reentrant_lock_for_key(self, name: str) -> Iterator[None]:
with _CountedFileLock(str(self.path / f"{name}.lock")):
yield
class NoOpFileLock(PathLockBase):
def __enter__(self) -> None:
raise NotImplementedError
def __exit__(
self, exc_type: type[BaseException] | None, exc_val: BaseException | None, exc_tb: TracebackType | None
) -> None:
raise NotImplementedError
@contextmanager
def lock_for_key(self, name: str, no_block: bool = False) -> Iterator[None]: # noqa: ARG002, FBT002
yield
@contextmanager
def non_reentrant_lock_for_key(self, name: str) -> Iterator[None]: # noqa: ARG002
yield
__all__ = [
"NoOpFileLock",
"ReentrantFileLock",
"Timeout",
]
@@ -0,0 +1,16 @@
from __future__ import annotations
from ._permission import make_exe, set_tree
from ._sync import copy, copytree, ensure_dir, safe_delete, symlink
from ._win import get_short_path_name
__all__ = [
"copy",
"copytree",
"ensure_dir",
"get_short_path_name",
"make_exe",
"safe_delete",
"set_tree",
"symlink",
]
@@ -0,0 +1,34 @@
from __future__ import annotations
import os
from stat import S_IXGRP, S_IXOTH, S_IXUSR
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from pathlib import Path
def make_exe(filename: Path) -> None:
original_mode = filename.stat().st_mode
levels = [S_IXUSR, S_IXGRP, S_IXOTH]
for at in range(len(levels), 0, -1):
try:
mode = original_mode
for level in levels[:at]:
mode |= level
filename.chmod(mode)
break
except OSError:
continue
def set_tree(folder: Path, stat: int) -> None:
for root, _, files in os.walk(str(folder)):
for filename in files:
os.chmod(os.path.join(root, filename), stat)
__all__ = (
"make_exe",
"set_tree",
)
@@ -0,0 +1,90 @@
from __future__ import annotations
import logging
import os
import shutil
import sys
from stat import S_IWUSR
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from pathlib import Path
LOGGER = logging.getLogger(__name__)
def ensure_dir(path: Path) -> None:
if not path.exists():
LOGGER.debug("create folder %s", path)
os.makedirs(str(path))
def ensure_safe_to_do(src: Path, dest: Path) -> None:
if src == dest:
msg = f"source and destination is the same {src}"
raise ValueError(msg)
if not dest.exists():
return
if dest.is_dir() and not dest.is_symlink():
LOGGER.debug("remove directory %s", dest)
safe_delete(dest)
else:
LOGGER.debug("remove file %s", dest)
dest.unlink()
def symlink(src: Path, dest: Path) -> None:
ensure_safe_to_do(src, dest)
LOGGER.debug("symlink %s", _Debug(src, dest))
dest.symlink_to(src, target_is_directory=src.is_dir())
def copy(src: Path, dest: Path) -> None:
ensure_safe_to_do(src, dest)
is_dir = src.is_dir()
method = copytree if is_dir else shutil.copy
LOGGER.debug("copy %s", _Debug(src, dest))
method(str(src), str(dest))
def copytree(src: str, dest: str) -> None:
for root, _, files in os.walk(src):
dest_dir = os.path.join(dest, os.path.relpath(root, src))
if not os.path.isdir(dest_dir):
os.makedirs(dest_dir)
for name in files:
src_f = os.path.join(root, name)
dest_f = os.path.join(dest_dir, name)
shutil.copy(src_f, dest_f)
def safe_delete(dest: Path) -> None:
def onerror(func: object, path: str, exc_info: object) -> None: # noqa: ARG001
if not os.access(path, os.W_OK):
os.chmod(path, S_IWUSR)
func(path) # ty: ignore[call-non-callable]
else:
raise # noqa: PLE0704
if sys.version_info >= (3, 12):
shutil.rmtree(str(dest), ignore_errors=True, onexc=onerror)
else:
shutil.rmtree(str(dest), ignore_errors=True, onerror=onerror)
class _Debug:
def __init__(self, src: Path, dest: Path) -> None:
self.src = src
self.dest = dest
def __str__(self) -> str:
return f"{'directory ' if self.src.is_dir() else ''}{self.src!s} to {self.dest!s}"
__all__ = [
"copy",
"copytree",
"ensure_dir",
"safe_delete",
"symlink",
]
@@ -0,0 +1,23 @@
from __future__ import annotations
def get_short_path_name(long_name: str) -> str:
"""Gets the short path name of a given long path - http://stackoverflow.com/a/23598461/200291."""
import ctypes # noqa: PLC0415
from ctypes import wintypes # noqa: PLC0415
GetShortPathNameW = ctypes.windll.kernel32.GetShortPathNameW # noqa: N806 # ty: ignore[unresolved-attribute]
GetShortPathNameW.argtypes = [wintypes.LPCWSTR, wintypes.LPWSTR, wintypes.DWORD]
GetShortPathNameW.restype = wintypes.DWORD
output_buf_size = 0
while True:
output_buf = ctypes.create_unicode_buffer(output_buf_size)
needed = GetShortPathNameW(long_name, output_buf, output_buf_size)
if output_buf_size >= needed:
return output_buf.value
output_buf_size = needed
__all__ = [
"get_short_path_name",
]
@@ -0,0 +1,48 @@
from __future__ import annotations
import subprocess
from shlex import quote
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from collections.abc import Mapping
CREATE_NO_WINDOW = 0x80000000
class LogCmd:
def __init__(self, cmd: list[str], env: Mapping[str, str] | None = None) -> None:
self.cmd = cmd
self.env = env
def __repr__(self) -> str:
cmd_repr = " ".join(quote(str(c)) for c in self.cmd)
if self.env is not None:
cmd_repr = f"{cmd_repr} env of {self.env!r}"
return cmd_repr
def run_cmd(cmd: list[str]) -> tuple[int, str, str]:
try:
process = subprocess.Popen(
cmd,
universal_newlines=True,
stdin=subprocess.PIPE,
stderr=subprocess.PIPE,
stdout=subprocess.PIPE,
encoding="utf-8",
)
out, err = process.communicate() # input disabled
code = process.returncode
except OSError as error:
code, out, err = error.errno, "", error.strerror
if code == 2 and err is not None and "file" in err: # noqa: PLR2004
err = str(error) # FileNotFoundError in Python >= 3.3
return code, out, err # ty: ignore[invalid-return-type]
__all__ = (
"CREATE_NO_WINDOW",
"LogCmd",
"run_cmd",
)
@@ -0,0 +1,45 @@
from __future__ import annotations
import logging
import zipfile
from pathlib import Path
from virtualenv.info import ROOT
LOGGER = logging.getLogger(__name__)
def read(full_path: str | Path) -> str:
sub_file = _get_path_within_zip(full_path)
with zipfile.ZipFile(ROOT, "r") as zip_file, zip_file.open(sub_file) as file_handler:
return file_handler.read().decode("utf-8")
def extract(full_path: str | Path, dest: Path) -> None:
LOGGER.debug("extract %s to %s", full_path, dest)
sub_file = _get_path_within_zip(full_path)
with zipfile.ZipFile(ROOT, "r") as zip_file:
info = zip_file.getinfo(sub_file)
info.filename = dest.name
zip_file.extract(info, str(dest.parent))
def _get_path_within_zip(full_path: str | Path) -> str:
# Use Path.relative_to so symlinks and ``..`` segments cannot slip through a string ``startswith`` check. The zipapp
# root is a real file we own so ``resolve`` is safe; anything that does not resolve under ROOT is a bug or an
# attempt to escape the archive and we refuse it.
resolved = Path(full_path).resolve()
root = Path(ROOT).resolve()
try:
relative = resolved.relative_to(root)
except ValueError as exc:
msg = f"full_path={resolved} should be within ROOT={root}"
raise RuntimeError(msg) from exc
# Zip entries always use forward slashes regardless of platform.
return relative.as_posix()
__all__ = [
"extract",
"read",
]