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,160 @@
from __future__ import annotations
import os
import shutil
from argparse import SUPPRESS, ArgumentDefaultsHelpFormatter, ArgumentParser, Namespace
from collections import OrderedDict
from typing import TYPE_CHECKING, Any
if TYPE_CHECKING:
from argparse import Action
from collections.abc import Mapping, Sequence
from virtualenv.config.convert import get_type
from virtualenv.config.env_var import get_env_var
from virtualenv.config.ini import IniConfig
class VirtualEnvOptions(Namespace):
def __init__(self, **kwargs: Any) -> None: # noqa: ANN401
super().__init__(**kwargs)
self._src: str | None = None
self._sources: dict[str, str] = {}
def set_src(self, key: str, value: Any, src: str) -> None: # noqa: ANN401
"""Set an option value and record where it came from.
:param key: the option name
:param value: the option value
:param src: the source of the value (e.g. ``"cli"``, ``"env var"``, ``"default"``)
"""
setattr(self, key, value)
if src.startswith("env var"):
src = "env var"
self._sources[key] = src
def __setattr__(self, key: str, value: Any) -> None: # noqa: ANN401
if (src := getattr(self, "_src", None)) is not None:
self._sources[key] = src
super().__setattr__(key, value)
def get_source(self, key: str) -> str | None:
"""Return the source that provided a given option value.
:param key: the option name
:returns: the source string (e.g. ``"cli"``, ``"env var"``, ``"default"``), or ``None`` if not tracked
"""
return self._sources.get(key)
@property
def verbosity(self) -> int | None:
"""The verbosity level, computed as ``verbose - quiet``, clamped to zero.
:returns: the verbosity level, or ``None`` if neither ``--verbose`` nor ``--quiet`` has been parsed yet
"""
if not hasattr(self, "verbose") and not hasattr(self, "quiet"):
return None
return max(self.verbose - self.quiet, 0)
def __repr__(self) -> str:
return f"{type(self).__name__}({', '.join(f'{k}={v}' for k, v in vars(self).items() if not k.startswith('_'))})"
class VirtualEnvConfigParser(ArgumentParser):
"""Custom option parser which updates its defaults by checking the configuration files and environmental vars."""
def __init__(
self,
options: VirtualEnvOptions | None = None,
env: Mapping[str, str] | None = None,
*args: Any, # noqa: ANN401
**kwargs: Any, # noqa: ANN401
) -> None:
env = os.environ if env is None else env
self.file_config = IniConfig(env)
self.epilog_list = []
self.env = env
kwargs["epilog"] = self.file_config.epilog
kwargs["add_help"] = False
kwargs["formatter_class"] = HelpFormatter
kwargs["prog"] = "virtualenv"
super().__init__(*args, **kwargs)
self._fixed = set()
if options is not None and not isinstance(options, VirtualEnvOptions):
msg = "options must be of type VirtualEnvOptions"
raise TypeError(msg)
self.options = VirtualEnvOptions() if options is None else options
self._interpreter = None
self._app_data = None
def _fix_defaults(self) -> None:
for action in self._actions:
action_id = id(action)
if action_id not in self._fixed:
self._fix_default(action)
self._fixed.add(action_id)
def _fix_default(self, action: Action) -> None:
if hasattr(action, "default") and hasattr(action, "dest") and action.default != SUPPRESS:
as_type = get_type(action)
names = OrderedDict((i.lstrip("-").replace("-", "_"), None) for i in action.option_strings)
outcome = None
for name in names:
outcome = get_env_var(name, as_type, self.env)
if outcome is not None:
break
if outcome is None and self.file_config:
for name in names:
outcome = self.file_config.get(name, as_type)
if outcome is not None:
break
if outcome is not None:
action.default, default_source = outcome
vars(action)["default_source"] = default_source
else:
outcome = action.default, "default"
self.options.set_src(action.dest, *outcome)
def enable_help(self) -> None:
self._fix_defaults()
self.add_argument("-h", "--help", action="help", default=SUPPRESS, help="show this help message and exit")
def parse_known_args( # ty: ignore[invalid-method-override]
self, args: Sequence[str] | None = None, namespace: VirtualEnvOptions | None = None
) -> tuple[VirtualEnvOptions, list[str]]:
if namespace is None:
namespace = self.options
elif namespace is not self.options:
msg = "can only pass in parser.options"
raise ValueError(msg)
self._fix_defaults()
self.options._src = "cli" # noqa: SLF001
try:
namespace.env = self.env
return super().parse_known_args(args, namespace=namespace)
finally:
self.options._src = None # noqa: SLF001
class HelpFormatter(ArgumentDefaultsHelpFormatter):
def __init__(self, prog: str, **kwargs: Any) -> None: # noqa: ANN401
super().__init__(prog, max_help_position=32, width=shutil.get_terminal_size().columns, **kwargs)
def _get_help_string(self, action: Action) -> str | None:
text = super()._get_help_string(action)
if text is not None and hasattr(action, "default_source"):
default = " (default: %(default)s)"
if text.endswith(default):
text = f"{text[: -len(default)]} (default: %(default)s -> from %(default_source)s)"
return text
__all__ = [
"HelpFormatter",
"VirtualEnvConfigParser",
"VirtualEnvOptions",
]
@@ -0,0 +1,105 @@
from __future__ import annotations
import logging
import os
from typing import TYPE_CHECKING, ClassVar
if TYPE_CHECKING:
from argparse import Action
from typing import Any
LOGGER = logging.getLogger(__name__)
class TypeData:
def __init__(self, default_type: type, as_type: type) -> None:
self.default_type = default_type
self.as_type = as_type
def __repr__(self) -> str:
return f"{self.__class__.__name__}(base={self.default_type}, as={self.as_type})"
def convert(self, value: str) -> Any: # noqa: ANN401
return self.default_type(value)
class BoolType(TypeData):
BOOLEAN_STATES: ClassVar[dict[str, bool]] = {
"1": True,
"yes": True,
"true": True,
"on": True,
"0": False,
"no": False,
"false": False,
"off": False,
}
def convert(self, value: str) -> bool:
if value.lower() not in self.BOOLEAN_STATES:
msg = f"Not a boolean: {value}"
raise ValueError(msg)
return self.BOOLEAN_STATES[value.lower()]
class NoneType(TypeData):
def convert(self, value: str) -> str | None:
if not value:
return None
return str(value)
class ListType(TypeData):
def _validate(self) -> None:
"""no op."""
def convert(self, value: str | list[str], flatten: bool = True) -> list[Any]: # noqa: ARG002, FBT002
values = self.split_values(value)
result = []
for a_value in values:
sub_values = a_value.split(os.pathsep)
result.extend(sub_values)
return [self.as_type(i) for i in result]
def split_values(self, value: str | bytes | list[str]) -> list[str]:
"""Split the provided value into a list.
First this is done by newlines. If there were no newlines in the text, then we next try to split by comma.
"""
if isinstance(value, (str, bytes)):
# Use `splitlines` rather than a custom check for whether there is
# more than one line. This ensures that the full `splitlines()`
# logic is supported here.
values = value.splitlines()
if len(values) <= 1:
values = value.split(",") # ty: ignore[invalid-argument-type]
values = filter(None, [x.strip() for x in values])
else:
values = list(value)
return values # ty: ignore[invalid-return-type]
def convert(value: str, as_type: TypeData, source: str) -> Any: # noqa: ANN401
"""Convert the value as a given type where the value comes from the given source."""
try:
return as_type.convert(value)
except Exception as exception:
LOGGER.warning("%s failed to convert %r as %r because %r", source, value, as_type, exception)
raise
_CONVERT = {bool: BoolType, type(None): NoneType, list: ListType}
def get_type(action: Action) -> TypeData:
default_type = type(action.default)
as_type = default_type if action.type is None else action.type
return _CONVERT.get(default_type, TypeData)(default_type, as_type) # ty: ignore[invalid-argument-type]
__all__ = [
"convert",
"get_type",
]
@@ -0,0 +1,38 @@
from __future__ import annotations
from contextlib import suppress
from typing import TYPE_CHECKING
from .convert import convert
if TYPE_CHECKING:
from collections.abc import Mapping
from typing import Any
from .convert import TypeData
def get_env_var(key: str, as_type: TypeData, env: Mapping[str, str]) -> tuple[Any, str] | None:
"""Get the environment variable option.
:param key: the config key requested
:param as_type: the type we would like to convert it to
:param env: environment variables to use
:returns: the converted value and source, or None if not set
"""
environ_key = f"VIRTUALENV_{key.upper()}"
if env.get(environ_key):
value = env[environ_key]
with suppress(Exception): # note the converter already logs a warning when failures happen
source = f"env var {environ_key}"
as_type = convert(value, as_type, source)
return as_type, source
return None
__all__ = [
"get_env_var",
]
@@ -0,0 +1,83 @@
from __future__ import annotations
import logging
import os
from configparser import ConfigParser
from pathlib import Path
from typing import TYPE_CHECKING, ClassVar
from platformdirs import user_config_dir
from .convert import convert
if TYPE_CHECKING:
from collections.abc import Mapping
from typing import Any
from .convert import TypeData
LOGGER = logging.getLogger(__name__)
class IniConfig:
VIRTUALENV_CONFIG_FILE_ENV_VAR: ClassVar[str] = "VIRTUALENV_CONFIG_FILE"
STATE: ClassVar[dict[bool | None, str]] = {None: "failed to parse", True: "active", False: "missing"}
section = "virtualenv"
def __init__(self, env: Mapping[str, str] | None = None) -> None:
env = os.environ if env is None else env
config_file = env.get(self.VIRTUALENV_CONFIG_FILE_ENV_VAR, None)
self.is_env_var = config_file is not None
if config_file is None:
config_file = Path(user_config_dir(appname="virtualenv", appauthor="pypa")) / "virtualenv.ini"
else:
config_file = Path(config_file)
self.config_file = config_file
self._cache = {}
exception = None
self.has_config_file = None
try:
self.has_config_file = self.config_file.exists()
except OSError as exc:
exception = exc
else:
if self.has_config_file:
self.config_file = self.config_file.resolve()
self.config_parser = ConfigParser()
try:
self._load()
self.has_virtualenv_section = self.config_parser.has_section(self.section)
except Exception as exc: # noqa: BLE001
exception = exc
if exception is not None:
LOGGER.error("failed to read config file %s because %r", config_file, exception)
def _load(self) -> None:
with self.config_file.open("rt", encoding="utf-8") as file_handler:
return self.config_parser.read_file(file_handler)
def get(self, key: str, as_type: TypeData) -> tuple[Any, str] | None:
cache_key = key, as_type
if cache_key in self._cache:
return self._cache[cache_key]
try:
source = "file"
raw_value = self.config_parser.get(self.section, key.lower())
value = convert(raw_value, as_type, source)
result = value, source
except Exception: # noqa: BLE001
result = None
self._cache[cache_key] = result
return result
def __bool__(self) -> bool:
return bool(self.has_config_file) and bool(self.has_virtualenv_section)
@property
def epilog(self) -> str:
return (
f"\nconfig file {self.config_file} {self.STATE[self.has_config_file]} "
f"(change{'d' if self.is_env_var else ''} via env var {self.VIRTUALENV_CONFIG_FILE_ENV_VAR})"
)