diff --git a/src/judge0/__init__.py b/src/judge0/__init__.py index 18a7013..5ccf40b 100644 --- a/src/judge0/__init__.py +++ b/src/judge0/__init__.py @@ -113,6 +113,8 @@ def _get_implicit_client(flavor: Flavor) -> Client: CE = Flavor.CE EXTRA_CE = Flavor.EXTRA_CE +# TODO: Let's use getattr and setattr for this language ALIASES and raise an +# exception if a value already exists. PYTHON = LanguageAlias.PYTHON CPP = LanguageAlias.CPP JAVA = LanguageAlias.JAVA diff --git a/src/judge0/clients.py b/src/judge0/clients.py index ada06cf..a9a63bb 100644 --- a/src/judge0/clients.py +++ b/src/judge0/clients.py @@ -6,6 +6,7 @@ from .data import LANGUAGE_TO_LANGUAGE_ID from .retry import RetryStrategy from .submission import Submission, Submissions +from .utils import handle_too_many_requests_error_for_preview_client class Client: @@ -22,6 +23,7 @@ def __init__( self.auth_headers = auth_headers self.retry_strategy = retry_strategy + # TODO: Should be handled differently. try: self.languages = tuple(Language(**lang) for lang in self.get_languages()) self.config = Config(**self.get_config_info()) @@ -30,6 +32,7 @@ def __init__( f"Authentication failed. Visit {self.HOME_URL} to get or review your authentication credentials." ) from e + @handle_too_many_requests_error_for_preview_client def get_about(self) -> dict: r = requests.get( f"{self.endpoint}/about", @@ -38,6 +41,7 @@ def get_about(self) -> dict: r.raise_for_status() return r.json() + @handle_too_many_requests_error_for_preview_client def get_config_info(self) -> dict: r = requests.get( f"{self.endpoint}/config_info", @@ -46,18 +50,21 @@ def get_config_info(self) -> dict: r.raise_for_status() return r.json() + @handle_too_many_requests_error_for_preview_client def get_language(self, language_id) -> dict: request_url = f"{self.endpoint}/languages/{language_id}" r = requests.get(request_url, headers=self.auth_headers) r.raise_for_status() return r.json() + @handle_too_many_requests_error_for_preview_client def get_languages(self) -> list[dict]: request_url = f"{self.endpoint}/languages" r = requests.get(request_url, headers=self.auth_headers) r.raise_for_status() return r.json() + @handle_too_many_requests_error_for_preview_client def get_statuses(self) -> list[dict]: r = requests.get( f"{self.endpoint}/statuses", @@ -74,7 +81,7 @@ def version(self): return self._version def get_language_id(self, language: Union[LanguageAlias, int]) -> int: - """Get language id for the corresponding language alias for the client.""" + """Get language id corresponding to the language alias for the client.""" if isinstance(language, LanguageAlias): supported_language_ids = LANGUAGE_TO_LANGUAGE_ID[self.version] language = supported_language_ids.get(language, -1) @@ -85,6 +92,7 @@ def is_language_supported(self, language: Union[LanguageAlias, int]) -> bool: language_id = self.get_language_id(language) return any(language_id == lang.id for lang in self.languages) + @handle_too_many_requests_error_for_preview_client def create_submission(self, submission: Submission) -> Submission: # Check if the client supports the language specified in the submission. if not self.is_language_supported(language=submission.language): @@ -112,6 +120,7 @@ def create_submission(self, submission: Submission) -> Submission: return submission + @handle_too_many_requests_error_for_preview_client def get_submission( self, submission: Submission, @@ -143,6 +152,7 @@ def get_submission( return submission + @handle_too_many_requests_error_for_preview_client def create_submissions(self, submissions: Submissions) -> Submissions: # Check if all submissions contain supported language. for submission in submissions: @@ -167,6 +177,7 @@ def create_submissions(self, submissions: Submissions) -> Submissions: return submissions + @handle_too_many_requests_error_for_preview_client def get_submissions( self, submissions: Submissions, diff --git a/src/judge0/utils.py b/src/judge0/utils.py new file mode 100644 index 0000000..184c368 --- /dev/null +++ b/src/judge0/utils.py @@ -0,0 +1,47 @@ +"""Module containing different utility functions for Judge0 Python SDK.""" + +from functools import wraps +from http import HTTPStatus + +from requests import HTTPError + + +def is_http_too_many_requests_error(exception: Exception) -> bool: + return ( + isinstance(exception, HTTPError) + and exception.response is not None + and exception.response.status_code == HTTPStatus.TOO_MANY_REQUESTS + ) + + +def handle_too_many_requests_error_for_preview_client(func): + @wraps(func) + def wrapper(*args, **kwargs): + try: + return func(*args, **kwargs) + except HTTPError as err: + if is_http_too_many_requests_error(exception=err): + # If the raised exception is inside the one of the Sulu clients + # let's check if we are dealing with the implicit client. + if args: + instance = args[0] + class_name = instance.__class__.__name__ + # Check if we are using a preview version of the client. + if ( + class_name in ("SuluJudge0CE", "SuluJudge0ExtraCE") + and instance.api_key is None + ): + raise RuntimeError( + "You are using a preview version of the Sulu " + "clients and you've hit a rate limit on the preview " + f"clients. Visit {instance.HOME_URL} to get or " + "review your authentication credentials." + ) from err + else: + raise err from None + else: + raise err from None + except Exception as err: + raise err from None + + return wrapper