Current File : /home/inlingua/miniconda3/lib/python3.1/site-packages/conda/gateways/repodata/jlap/interface.py |
# Copyright (C) 2012 Anaconda, Inc
# SPDX-License-Identifier: BSD-3-Clause
"""JLAP interface for repodata."""
from __future__ import annotations
import logging
import os
from typing import TYPE_CHECKING
from ....base.context import context
from ...connection.download import disable_ssl_verify_warning
from ...connection.session import get_session
from .. import (
CACHE_CONTROL_KEY,
ETAG_KEY,
LAST_MODIFIED_KEY,
URL_KEY,
RepodataOnDisk,
RepodataState,
RepoInterface,
Response304ContentUnchanged,
conda_http_errors,
)
from . import fetch
if TYPE_CHECKING:
from .. import RepodataCache
log = logging.getLogger(__name__)
class JlapRepoInterface(RepoInterface):
def __init__(
self,
url: str,
repodata_fn: str | None,
*,
cache: RepodataCache,
**kwargs,
) -> None:
log.debug("Using %s", self.__class__.__name__)
self._cache = cache
self._url = url
self._repodata_fn = repodata_fn
self._log = logging.getLogger(__name__)
self._stderrlog = logging.getLogger("conda.stderrlog")
def repodata(self, state: dict | RepodataState) -> str | None:
"""
Fetch newest repodata if necessary.
Always writes to ``cache_path_json``.
"""
self.repodata_parsed(state)
raise RepodataOnDisk()
def repodata_parsed(self, state: dict | RepodataState) -> dict | None:
"""
JLAP has to parse the JSON anyway.
Use this to avoid a redundant parse when repodata is updated.
When repodata is not updated, it doesn't matter whether this function or
the caller reads from a file.
"""
session = get_session(self._url)
if not context.ssl_verify:
disable_ssl_verify_warning()
repodata_url = f"{self._url}/{self._repodata_fn}"
# XXX won't modify caller's state dict
state_ = self._repodata_state_copy(state)
# at this point, self._cache.state == state == state_
temp_path = (
self._cache.cache_dir / f"{self._cache.name}.{os.urandom(2).hex()}.tmp"
)
try:
with conda_http_errors(self._url, self._repodata_fn):
repodata_json_or_none = fetch.request_url_jlap_state(
repodata_url,
state_,
session=session,
cache=self._cache,
temp_path=temp_path,
)
# update caller's state dict-or-RepodataState. Do this before
# the self._cache.replace() call which also writes state, then
# signal not to write state to caller.
state.update(state_)
state[URL_KEY] = self._url
headers = state.get("jlap", {}).get(
"headers"
) # XXX overwrite headers in jlapper.request_url_jlap_state
if headers:
state[ETAG_KEY] = headers.get("etag")
state[LAST_MODIFIED_KEY] = headers.get("last-modified")
state[CACHE_CONTROL_KEY] = headers.get("cache-control")
self._cache.state.update(state)
if temp_path.exists():
self._cache.replace(temp_path)
except fetch.Jlap304NotModified:
raise Response304ContentUnchanged()
finally:
# Clean up the temporary file. In the successful case it raises
# OSError as self._cache_replace() removed temp_file.
try:
temp_path.unlink()
except OSError:
pass
if repodata_json_or_none is None: # common
# Indicate that subdir_data mustn't rewrite cache_path_json
raise RepodataOnDisk()
else:
return repodata_json_or_none
def _repodata_state_copy(self, state: dict | RepodataState):
return RepodataState(dict=state)
class RepodataStateSkipFormat(RepodataState):
skip_formats: set[str]
def __init__(self, *args, skip_formats=set(), **kwargs):
super().__init__(*args, **kwargs)
self.skip_formats = set(skip_formats)
def should_check_format(self, format):
if format in self.skip_formats:
return False
return super().should_check_format(format)
class ZstdRepoInterface(JlapRepoInterface):
"""
Support repodata.json.zst (if available) without checking .jlap
"""
def _repodata_state_copy(self, state: dict | RepodataState):
return RepodataStateSkipFormat(dict=state, skip_formats=["jlap"])