Skip to content

Commit a3b359b

Browse files
committed
Sonar analysis
1 parent 5daf36f commit a3b359b

3 files changed

Lines changed: 754 additions & 494 deletions

File tree

py_src/fusion/embeddings.py

Lines changed: 180 additions & 106 deletions
Original file line numberDiff line numberDiff line change
@@ -285,6 +285,20 @@ def __init__( # noqa: PLR0912, PLR0913, PLR0915
285285
**kwargs,
286286
)
287287

288+
self._configure_sync_connection(http_auth, verify_certs, ssl_show_warn, ca_certs, client_cert, client_key)
289+
self.url_prefix, self.multi_dataset_url_prefix = _resolve_url_prefix(self.catalog, self.knowledge_base)
290+
291+
self.index_name: str | None = kwargs.get("index")
292+
293+
def _configure_sync_connection(
294+
self,
295+
http_auth: Any,
296+
verify_certs: bool,
297+
ssl_show_warn: bool,
298+
ca_certs: Any,
299+
client_cert: Any,
300+
client_key: Any,
301+
) -> None:
288302
if not self.http_compress:
289303
# Need to set this to 'None' otherwise Requests adds its own.
290304
self.session.headers["accept-encoding"] = None # type: ignore
@@ -313,9 +327,6 @@ def __init__( # noqa: PLR0912, PLR0913, PLR0915
313327

314328
if self.use_ssl and not verify_certs and ssl_show_warn:
315329
warnings.warn(f"Connecting to {self.host} using SSL with verify_certs=False is insecure.", stacklevel=2)
316-
self.url_prefix, self.multi_dataset_url_prefix = _resolve_url_prefix(self.catalog, self.knowledge_base)
317-
318-
self.index_name: str | None = kwargs.get("index")
319330

320331
def _tidy_url(self, url: str) -> str:
321332
return url.replace("%2F%7B", "/").replace("%7D%2F", "/").replace("%2F", "/")
@@ -576,40 +587,15 @@ def __init__( # noqa: PLR0912, PLR0913, PLR0915
576587
)
577588

578589
self.ssl_assert_fingerprint = ssl_assert_fingerprint
579-
if self.use_ssl and ssl_context is None:
580-
ssl_context = (
581-
create_secure_ssl_context()
582-
if ssl_version is None
583-
else ssl.SSLContext(ssl_version) # NOSONAR (S4423): preserve caller-supplied protocol
584-
)
585-
586-
verify_certs, ssl_show_warn = _resolve_async_ssl_defaults(verify_certs, ssl_show_warn)
587-
588-
if verify_certs:
589-
ssl_context.verify_mode = ssl.CERT_REQUIRED
590-
ssl_context.check_hostname = True
591-
else:
592-
ssl_context.check_hostname = False
593-
ssl_context.verify_mode = ssl.CERT_NONE
594-
595-
ca_certs = self.default_ca_certs() if ca_certs is None else ca_certs
596-
if verify_certs:
597-
if not ca_certs:
598-
raise ImproperlyConfigured(
599-
"Root certificates are missing for certificate "
600-
"validation. Either pass them in using the ca_certs parameter or "
601-
"install certifi to use it automatically."
602-
)
603-
_load_verify_locations(ssl_context, ca_certs)
604-
elif ssl_show_warn:
605-
warnings.warn(f"Connecting to {self.host} using SSL with verify_certs=False is insecure.", stacklevel=2)
606-
607-
_validate_client_cert_path(client_cert, "client_cert")
608-
_validate_client_cert_path(client_key, "client_key")
609-
if client_cert and client_key:
610-
ssl_context.load_cert_chain(client_cert, client_key)
611-
elif client_cert:
612-
ssl_context.load_cert_chain(client_cert)
590+
ssl_context = self._build_async_ssl_context(
591+
ssl_context,
592+
verify_certs,
593+
ssl_show_warn,
594+
ca_certs,
595+
client_cert,
596+
client_key,
597+
ssl_version,
598+
)
613599

614600
self.headers.setdefault("connection", "keep-alive")
615601
self.loop = loop
@@ -624,6 +610,159 @@ def __init__( # noqa: PLR0912, PLR0913, PLR0915
624610
self._http_auth = http_auth
625611
self._ssl_context = ssl_context
626612

613+
def _build_async_ssl_context(
614+
self,
615+
ssl_context: Any,
616+
verify_certs: Any,
617+
ssl_show_warn: Any,
618+
ca_certs: Any,
619+
client_cert: Any,
620+
client_key: Any,
621+
ssl_version: Any,
622+
) -> Any:
623+
if not self.use_ssl or ssl_context is not None:
624+
return ssl_context
625+
626+
ssl_context = (
627+
create_secure_ssl_context()
628+
if ssl_version is None
629+
else ssl.SSLContext(ssl_version) # NOSONAR (S4423): preserve caller-supplied protocol
630+
)
631+
verify_certs, ssl_show_warn = _resolve_async_ssl_defaults(verify_certs, ssl_show_warn)
632+
self._configure_async_ssl_context(ssl_context, verify_certs, ssl_show_warn, ca_certs)
633+
self._load_async_client_cert_chain(ssl_context, client_cert, client_key)
634+
return ssl_context
635+
636+
def _configure_async_ssl_context(
637+
self,
638+
ssl_context: ssl.SSLContext,
639+
verify_certs: bool,
640+
ssl_show_warn: bool,
641+
ca_certs: Any,
642+
) -> None:
643+
if verify_certs:
644+
ssl_context.verify_mode = ssl.CERT_REQUIRED
645+
ssl_context.check_hostname = True
646+
else:
647+
ssl_context.check_hostname = False
648+
ssl_context.verify_mode = ssl.CERT_NONE
649+
650+
ca_certs = self.default_ca_certs() if ca_certs is None else ca_certs
651+
if verify_certs:
652+
if not ca_certs:
653+
raise ImproperlyConfigured(
654+
"Root certificates are missing for certificate "
655+
"validation. Either pass them in using the ca_certs parameter or "
656+
"install certifi to use it automatically."
657+
)
658+
_load_verify_locations(ssl_context, ca_certs)
659+
elif ssl_show_warn:
660+
warnings.warn(f"Connecting to {self.host} using SSL with verify_certs=False is insecure.", stacklevel=2)
661+
662+
@staticmethod
663+
def _load_async_client_cert_chain(
664+
ssl_context: ssl.SSLContext,
665+
client_cert: Any,
666+
client_key: Any,
667+
) -> None:
668+
_validate_client_cert_path(client_cert, "client_cert")
669+
_validate_client_cert_path(client_key, "client_key")
670+
if client_cert and client_key:
671+
ssl_context.load_cert_chain(client_cert, client_key)
672+
elif client_cert:
673+
ssl_context.load_cert_chain(client_cert)
674+
675+
def _prepare_async_request(
676+
self,
677+
method: str,
678+
url: str,
679+
params: Mapping[str, Any] | None,
680+
body: bytes | None,
681+
) -> tuple[str, str, bytes | None, bytes | None, str]:
682+
method = _normalize_method(method)
683+
url = self._tidy_url(url)
684+
url = self._make_url_valid(url, body)
685+
body = _prepare_embeddings_request_body(url, method, body, self.knowledge_base)
686+
orig_body = body
687+
query_string = urlencode(params) if params else ""
688+
if query_string:
689+
url = f"{url}?{query_string}"
690+
return method, url, body, orig_body, query_string
691+
692+
def _build_async_request_headers(
693+
self,
694+
method: str,
695+
url: str,
696+
query_string: str,
697+
body: bytes | None,
698+
headers: Mapping[str, str] | None,
699+
) -> tuple[dict[str, Any], bytes | None, aiohttp.BasicAuth | None]:
700+
req_headers = self.headers.copy()
701+
if headers:
702+
req_headers.update(headers)
703+
704+
if self.http_compress and body:
705+
body = self._gzip_compress(body)
706+
req_headers["content-encoding"] = "gzip"
707+
708+
auth = self._http_auth if isinstance(self._http_auth, aiohttp.BasicAuth) else None
709+
if callable(self._http_auth):
710+
req_headers = {
711+
**req_headers,
712+
**self._http_auth(method, url, query_string, body),
713+
}
714+
return req_headers, body, auth
715+
716+
def _raise_async_request_exception(
717+
self,
718+
method: str,
719+
url: str,
720+
orig_body: bytes | None,
721+
start: float,
722+
exception: Exception,
723+
) -> None:
724+
self.log_request_fail(
725+
method,
726+
str(url),
727+
url,
728+
orig_body,
729+
self.loop.time() - start,
730+
exception=exception,
731+
)
732+
if isinstance(exception, aiohttp_exceptions.ServerFingerprintMismatch):
733+
raise SSLError("N/A", str(exception), exception) from exception
734+
if isinstance(exception, (asyncio.TimeoutError, aiohttp_exceptions.ServerTimeoutError)):
735+
raise ConnectionTimeout("TIMEOUT", str(exception), exception) from exception
736+
raise ConnectionError("N/A", str(exception), exception) from exception
737+
738+
def _handle_async_response(
739+
self,
740+
method: str,
741+
url: str,
742+
orig_body: bytes | None,
743+
response: Any,
744+
raw_data: str,
745+
duration: float,
746+
ignore: Collection[int],
747+
) -> None:
748+
warning_headers = response.headers.getall("warning", ())
749+
self._raise_warnings(warning_headers)
750+
751+
raw_data_modified = str(_modify_post_response_langchain(raw_data))
752+
if not (HTTP_OK <= response.status < HTTP_MULTIPLE_CHOICES) and response.status not in ignore:
753+
self.log_request_fail(
754+
method,
755+
str(url),
756+
url,
757+
orig_body,
758+
duration,
759+
status_code=response.status,
760+
response=raw_data_modified,
761+
)
762+
self._raise_error(response.status, raw_data_modified)
763+
764+
self.log_request_success(method, str(url), url, orig_body, response.status, raw_data_modified, duration)
765+
627766
def _tidy_url(self, url: str) -> str:
628767
return url.replace("%2F%7B", "/").replace("%7D%2F", "/").replace("%2F", "/")
629768

@@ -656,49 +795,16 @@ async def perform_request( # noqa: PLR0912, PLR0915
656795
await self._create_aiohttp_session()
657796
assert self.session is not None
658797

659-
method = _normalize_method(method)
660-
661-
url = self._tidy_url(url)
662-
url = self._make_url_valid(url, body)
798+
method, url, body, orig_body, query_string = self._prepare_async_request(method, url, params, body)
663799

664800
if _is_root_info_request(method, url, self.base_url):
665801
return _mock_info_response()
666802
skipped_response = _skip_unsupported_endpoint(url) or _skip_multi_kb_write(self.knowledge_base, method, body)
667803
if skipped_response is not None:
668804
return skipped_response
669805

670-
body = _prepare_embeddings_request_body(url, method, body, self.knowledge_base)
671-
orig_body = body
672-
query_string = urlencode(params) if params else ""
673-
674-
# Top-tier tip-toeing happening here. Basically
675-
# because Pip's old resolver is bad and wipes out
676-
# strict pins in favor of non-strict pins of extras
677-
# our [async] extra overrides aiohttp's pin of
678-
# yarl. yarl released breaking changes, aiohttp pinned
679-
# defensively afterwards, but our users don't get
680-
# that nice pin that aiohttp set. :( So to play around
681-
# this super-defensively we try to import yarl, if we can't
682-
# then we pass a string into ClientSession.request() instead.
683-
if query_string:
684-
url = f"{url}?{query_string}"
685-
686806
request_timeout = aiohttp.ClientTimeout(total=timeout if timeout is not None else self.timeout)
687-
688-
req_headers = self.headers.copy()
689-
if headers:
690-
req_headers.update(headers)
691-
692-
if self.http_compress and body:
693-
body = self._gzip_compress(body)
694-
req_headers["content-encoding"] = "gzip"
695-
696-
auth = self._http_auth if isinstance(self._http_auth, aiohttp.BasicAuth) else None
697-
if callable(self._http_auth):
698-
req_headers = {
699-
**req_headers,
700-
**self._http_auth(method, url, query_string, body),
701-
}
807+
req_headers, body, auth = self._build_async_request_headers(method, url, query_string, body, headers)
702808

703809
start = self.loop.time()
704810
timeout_obj: ClientTimeout | None = ClientTimeout(total=timeout) if isinstance(timeout, (int, float)) else None
@@ -719,41 +825,9 @@ async def perform_request( # noqa: PLR0912, PLR0915
719825
except reraise_exceptions:
720826
raise
721827
except Exception as e: # noqa: BLE001
722-
self.log_request_fail(
723-
method,
724-
str(url),
725-
url,
726-
orig_body,
727-
self.loop.time() - start,
728-
exception=e,
729-
)
730-
if isinstance(e, aiohttp_exceptions.ServerFingerprintMismatch):
731-
raise SSLError("N/A", str(e), e) from e
732-
if isinstance(e, (asyncio.TimeoutError, aiohttp_exceptions.ServerTimeoutError)):
733-
raise ConnectionTimeout("TIMEOUT", str(e), e) from e
734-
raise ConnectionError("N/A", str(e), e) from e
735-
736-
# raise warnings if any from the 'Warnings' header.
737-
warning_headers = response.headers.getall("warning", ())
738-
self._raise_warnings(warning_headers)
739-
740-
raw_data_modified = str(_modify_post_response_langchain(raw_data))
741-
742-
# raise errors based on http status codes, let the client handle those if needed
743-
if not (HTTP_OK <= response.status < HTTP_MULTIPLE_CHOICES) and response.status not in ignore:
744-
self.log_request_fail(
745-
method,
746-
str(url),
747-
url,
748-
orig_body,
749-
duration,
750-
status_code=response.status,
751-
response=raw_data_modified,
752-
)
753-
self._raise_error(response.status, raw_data_modified)
754-
755-
self.log_request_success(method, str(url), url, orig_body, response.status, raw_data_modified, duration)
828+
self._raise_async_request_exception(method, url, orig_body, start, e)
756829

830+
self._handle_async_response(method, url, orig_body, response, raw_data, duration, ignore)
757831
return response.status, response.headers, raw_data
758832

759833
async def close(self) -> Any:

0 commit comments

Comments
 (0)