class IniConfig(object): VIRTUALENV_CONFIG_FILE_ENV_VAR = six.ensure_str("VIRTUALENV_CONFIG_FILE") STATE = {None: "failed to parse", True: "active", False: "missing"} section = "virtualenv" def __init__(self): config_file = os.environ.get(self.VIRTUALENV_CONFIG_FILE_ENV_VAR, None) self.is_env_var = config_file is not None self.config_file = Path( config_file) if config_file is not None else DEFAULT_CONFIG_FILE self._cache = {} self.has_config_file = self.config_file.exists() if self.has_config_file: self.config_file = self.config_file.resolve() self.config_parser = ConfigParser.ConfigParser() try: self._load() self.has_virtualenv_section = self.config_parser.has_section( self.section) except Exception as exception: logging.error("failed to read config file %s because %r", config_file, exception) self.has_config_file = None def _load(self): with self.config_file.open("rt") as file_handler: reader = getattr(self.config_parser, "read_file" if PY3 else "readfp") reader(file_handler) def get(self, key, as_type): cache_key = key, as_type if cache_key in self._cache: return self._cache[cache_key] # noinspection PyBroadException 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: result = None self._cache[cache_key] = result return result def __bool__(self): return bool(self.has_config_file) and bool(self.has_virtualenv_section) @property def epilog(self): msg = "{}config file {} {} (change{} via env var {})" return msg.format( os.linesep, self.config_file, self.STATE[self.has_config_file], "d" if self.is_env_var else "", self.VIRTUALENV_CONFIG_FILE_ENV_VAR, )
def validate_dest(cls, raw_value): """No path separator in the path, valid chars and must be write-able""" def non_write_able(dest, value): common = Path(*os.path.commonprefix([value.parts, dest.parts])) raise ArgumentTypeError( "the destination {} is not write-able at {}".format(dest.relative_to(common), common) ) # the file system must be able to encode # note in newer CPython this is always utf-8 https://www.python.org/dev/peps/pep-0529/ encoding = sys.getfilesystemencoding() refused = OrderedDict() kwargs = {"errors": "ignore"} if encoding != "mbcs" else {} for char in six.ensure_text(raw_value): try: trip = char.encode(encoding, **kwargs).decode(encoding) if trip == char: continue raise ValueError(trip) except ValueError: refused[char] = None if refused: raise ArgumentTypeError( "the file system codec ({}) cannot handle characters {!r} within {!r}".format( encoding, "".join(refused.keys()), raw_value ) ) for char in (i for i in (os.pathsep, os.altsep) if i is not None): if char in raw_value: raise ArgumentTypeError( "destination {!r} must not contain the path separator ({}) as this would break " "the activation scripts".format(raw_value, char) ) value = Path(raw_value) if value.exists() and value.is_file(): raise ArgumentTypeError("the destination {} already exists and is a file".format(value)) if (3, 3) <= sys.version_info <= (3, 6): # pre 3.6 resolve is always strict, aka must exists, sidestep by using os.path operation dest = Path(os.path.realpath(raw_value)) else: dest = value.resolve() value = dest while dest: if dest.exists(): if os.access(six.ensure_text(str(dest)), os.W_OK): break else: non_write_able(dest, value) base, _ = dest.parent, dest.name if base == dest: non_write_able(dest, value) # pragma: no cover dest = base return str(value)
def norm_path(path): # python may return Windows short paths, normalize if not isinstance(path, Path): path = Path(path) path = ensure_text(str(path.resolve())) if sys.platform != "win32": result = path else: from ctypes import create_unicode_buffer, windll buffer_cont = create_unicode_buffer(256) get_long_path_name = windll.kernel32.GetLongPathNameW get_long_path_name(six.text_type(path), buffer_cont, 256) result = buffer_cont.value or path return normcase(result)
def __init__(self, folder): self._lock = None path = Path(folder) self.path = path.resolve() if path.exists() else path