├── .python-version ├── docs ├── assets │ └── logo.png ├── source │ ├── api │ │ ├── errors.rst │ │ ├── retry.rst │ │ ├── api.rst │ │ ├── filesystem.rst │ │ ├── submission.rst │ │ ├── types.rst │ │ └── clients.rst │ ├── in_depth │ │ ├── overview.rst │ │ └── client_resolution.rst │ ├── _templates │ │ └── versioning.html │ ├── contributors_guide │ │ ├── release_notes.rst │ │ └── contributing.rst │ ├── index.rst │ └── conf.py ├── Makefile └── make.bat ├── examples ├── 1000_http_callback_aka_webhook │ ├── requirements.txt │ ├── .github │ │ └── demo.gif │ ├── README.md │ └── main.py ├── 0004_hello_world.py ├── 0003_hello_world.py ├── 0001_hello_world.py ├── many_submissions.py ├── 0002_hello_world.py ├── test_cases_01.py ├── atd_clients.py ├── rapid_clients.py ├── atd_submission.py ├── rapid_submission.py ├── atd_submissions.py ├── rapid_submissions.py ├── 0005_filesystem.py ├── 0005_test_cases.py └── 0006_exe.py ├── CONTRIBUTING.md ├── RELEASE_NOTES_TEMPLATE.md ├── src └── judge0 │ ├── errors.py │ ├── common.py │ ├── utils.py │ ├── retry.py │ ├── filesystem.py │ ├── data.py │ ├── base_types.py │ ├── __init__.py │ ├── submission.py │ ├── api.py │ └── clients.py ├── .pre-commit-config.yaml ├── .github ├── ISSUE_TEMPLATE │ ├── feature_request.md │ └── bug_report.md └── workflows │ ├── publish.yml │ ├── test.yml │ └── docs.yml ├── .env.template ├── LICENSE ├── tests ├── test_clients.py ├── test_api.py ├── conftest.py ├── test_submission.py └── test_api_test_cases.py ├── pyproject.toml ├── .gitignore ├── CODE_OF_CONDUCT.md └── README.md /.python-version: -------------------------------------------------------------------------------- 1 | 3.10 -------------------------------------------------------------------------------- /docs/assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/judge0/judge0-python/HEAD/docs/assets/logo.png -------------------------------------------------------------------------------- /examples/1000_http_callback_aka_webhook/requirements.txt: -------------------------------------------------------------------------------- 1 | fastapi==0.115.4 2 | uvicorn==0.32.0 3 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # How to contribute 2 | 3 | See [docs](https://judge0.github.io/judge0-python/contributing.html). 4 | -------------------------------------------------------------------------------- /docs/source/api/errors.rst: -------------------------------------------------------------------------------- 1 | Errors Module 2 | ============= 3 | 4 | .. automodule:: judge0.errors 5 | :members: 6 | -------------------------------------------------------------------------------- /docs/source/api/retry.rst: -------------------------------------------------------------------------------- 1 | Retry Module 2 | ============ 3 | 4 | .. automodule:: judge0.retry 5 | :members: 6 | :member-order: bysource -------------------------------------------------------------------------------- /examples/0004_hello_world.py: -------------------------------------------------------------------------------- 1 | import judge0 2 | 3 | submission = judge0.run(source_code="print('Hello Judge0')") 4 | print(submission.stdout) 5 | -------------------------------------------------------------------------------- /docs/source/api/api.rst: -------------------------------------------------------------------------------- 1 | API Module 2 | ========== 3 | 4 | .. automodule:: judge0.api 5 | :members: 6 | :exclude-members: sync_run, async_run 7 | -------------------------------------------------------------------------------- /docs/source/api/filesystem.rst: -------------------------------------------------------------------------------- 1 | Filesystem Module 2 | ================= 3 | 4 | .. automodule:: judge0.filesystem 5 | :members: 6 | :member-order: bysource 7 | -------------------------------------------------------------------------------- /examples/1000_http_callback_aka_webhook/.github/demo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/judge0/judge0-python/HEAD/examples/1000_http_callback_aka_webhook/.github/demo.gif -------------------------------------------------------------------------------- /RELEASE_NOTES_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | # vX.Y.Z (YYYY-MM-DD) 2 | 3 | ## API Changes 4 | 5 | ## New Features 6 | 7 | ## Improvements 8 | 9 | ## Security Improvements 10 | 11 | ## Bug Fixes 12 | 13 | ## Security Fixes 14 | 15 | ## Other Changes 16 | -------------------------------------------------------------------------------- /examples/0003_hello_world.py: -------------------------------------------------------------------------------- 1 | import judge0 2 | 3 | submission = judge0.Submission( 4 | source_code="print('Hello Judge0')", 5 | language=judge0.PYTHON, 6 | ) 7 | 8 | submission = judge0.run(submissions=submission) 9 | 10 | print(submission.stdout) 11 | -------------------------------------------------------------------------------- /src/judge0/errors.py: -------------------------------------------------------------------------------- 1 | """Library specific errors.""" 2 | 3 | 4 | class PreviewClientLimitError(RuntimeError): 5 | """Limited usage of a preview client exceeded.""" 6 | 7 | 8 | class ClientResolutionError(RuntimeError): 9 | """Failed resolution of an unspecified client.""" 10 | -------------------------------------------------------------------------------- /docs/source/api/submission.rst: -------------------------------------------------------------------------------- 1 | Submission Module 2 | ================= 3 | 4 | .. automodule:: judge0.submission 5 | :members: 6 | :exclude-members: process_encoded_fields, process_language, process_post_execution_filesystem, 7 | process_status 8 | :member-order: groupwise 9 | -------------------------------------------------------------------------------- /examples/0001_hello_world.py: -------------------------------------------------------------------------------- 1 | import judge0 2 | 3 | submission = judge0.Submission( 4 | source_code="print('Hello Judge0')", 5 | language=100, 6 | ) 7 | 8 | # Run submission on CE flavor of judge0. 9 | submission = judge0.run(submissions=submission) 10 | 11 | print(submission.stdout) 12 | -------------------------------------------------------------------------------- /docs/source/in_depth/overview.rst: -------------------------------------------------------------------------------- 1 | Overview 2 | ======== 3 | 4 | TODO: 5 | 6 | * add a brief overview of the most important classes (Client, Submission, etc.) 7 | * add a brief overview of the most important functions (create_submission, get_submission, etc.) 8 | * write about the difference between high-level api and low-level api -------------------------------------------------------------------------------- /examples/many_submissions.py: -------------------------------------------------------------------------------- 1 | import judge0 2 | 3 | submissions = [] 4 | for i in range(42): 5 | submissions.append(judge0.Submission( 6 | source_code=f"print({i})", 7 | )) 8 | 9 | results = judge0.run(submissions=submissions) 10 | for result in results: 11 | print(result.stdout.strip(), " ", end="") 12 | print() 13 | -------------------------------------------------------------------------------- /examples/0002_hello_world.py: -------------------------------------------------------------------------------- 1 | import judge0 2 | 3 | submission = judge0.Submission( 4 | source_code="print('Hello Judge0')", 5 | language=25, 6 | ) 7 | 8 | # Instead of relying on the CE flavor of judge0, we can use EXTRA_CE. 9 | submission = judge0.run(client=judge0.EXTRA_CE, submissions=submission) 10 | 11 | print(submission.stdout) 12 | -------------------------------------------------------------------------------- /examples/test_cases_01.py: -------------------------------------------------------------------------------- 1 | import judge0 2 | 3 | 4 | submissions = judge0.run( 5 | source_code="print(f'hello {input()}')", 6 | test_cases=[ 7 | ("tuple", "hello tuple"), 8 | {"input": "dict", "expected_output": "hello dict"}, 9 | ["list", "hello list"], 10 | ], 11 | ) 12 | 13 | submissions = judge0.run(submissions=submissions) 14 | 15 | for submission in submissions: 16 | print(submission.status) 17 | -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | repos: 2 | - repo: https://github.com/omnilib/ufmt 3 | rev: v2.7.3 4 | hooks: 5 | - id: ufmt 6 | additional_dependencies: 7 | - black == 24.8.0 8 | - usort == 1.0.8.post1 9 | - repo: https://github.com/pycqa/flake8 10 | rev: 7.1.1 11 | hooks: 12 | - id: flake8 13 | additional_dependencies: 14 | - "flake8-pyproject" 15 | - flake8-docstrings 16 | - pydocstyle 17 | -------------------------------------------------------------------------------- /docs/source/_templates/versioning.html: -------------------------------------------------------------------------------- 1 | {% if versions %} 2 | {% set master_version = versions | selectattr('name', 'equalto', 'master') | list %} 3 | {% set other_versions = versions | rejectattr('name', 'equalto', 'master') | sort(attribute='name', reverse=true) %} 4 | {% set sorted_versions = master_version + other_versions %} 5 |

{{ _('Versions') }}

6 | 11 | {% endif %} -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /examples/atd_clients.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | import judge0 4 | from dotenv import load_dotenv 5 | 6 | load_dotenv() 7 | 8 | api_key = os.getenv("JUDGE0_ATD_API_KEY") 9 | 10 | client_ce = judge0.ATDJudge0CE(api_key=api_key) 11 | print(client_ce.get_about()) 12 | print(client_ce.get_config_info()) 13 | print(client_ce.get_statuses()) 14 | print(client_ce.get_languages()) 15 | print(client_ce.get_language(language_id=42)) 16 | 17 | client_extra_ce = judge0.ATDJudge0ExtraCE(api_key=api_key) 18 | print(client_extra_ce.get_about()) 19 | print(client_extra_ce.get_config_info()) 20 | print(client_extra_ce.get_statuses()) 21 | print(client_extra_ce.get_languages()) 22 | print(client_extra_ce.get_language(language_id=24)) 23 | -------------------------------------------------------------------------------- /examples/rapid_clients.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | import judge0 4 | from dotenv import load_dotenv 5 | 6 | load_dotenv() 7 | 8 | api_key = os.getenv("JUDGE0_RAPID_API_KEY") 9 | 10 | client_ce = judge0.RapidJudge0CE(api_key=api_key) 11 | print(client_ce.get_about()) 12 | print(client_ce.get_config_info()) 13 | print(client_ce.get_statuses()) 14 | print(client_ce.get_languages()) 15 | print(client_ce.get_language(language_id=42)) 16 | 17 | client_extra_ce = judge0.RapidJudge0ExtraCE(api_key=api_key) 18 | print(client_extra_ce.get_about()) 19 | print(client_extra_ce.get_config_info()) 20 | print(client_extra_ce.get_statuses()) 21 | print(client_extra_ce.get_languages()) 22 | print(client_extra_ce.get_language(language_id=24)) 23 | -------------------------------------------------------------------------------- /examples/atd_submission.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | import judge0 4 | 5 | from dotenv import load_dotenv 6 | 7 | load_dotenv() 8 | 9 | api_key = os.getenv("JUDGE0_ATD_API_KEY") 10 | 11 | 12 | def run_example(client_class, language_id): 13 | client = client_class(api_key=api_key) 14 | submission = judge0.Submission( 15 | source_code="print('Hello Judge0')", 16 | language=language_id, 17 | expected_output="Hello Judge0", 18 | ) 19 | 20 | judge0.execute(client=client, submissions=submission) 21 | 22 | print(f"{submission.status=}") 23 | print(f"{submission.stdout=}") 24 | 25 | 26 | def main(): 27 | run_example(judge0.ATDJudge0CE, 100) 28 | run_example(judge0.ATDJudge0ExtraCE, 25) 29 | 30 | 31 | if __name__ == "__main__": 32 | main() 33 | -------------------------------------------------------------------------------- /examples/rapid_submission.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | import judge0 4 | 5 | from dotenv import load_dotenv 6 | 7 | load_dotenv() 8 | 9 | api_key = os.getenv("JUDGE0_RAPID_API_KEY") 10 | 11 | 12 | def run_example(client_class, language_id): 13 | client = client_class(api_key=api_key) 14 | submission = judge0.Submission( 15 | source_code="print('Hello Judge0')", 16 | language=language_id, 17 | expected_output="Hello Judge0", 18 | ) 19 | 20 | judge0.execute(client=client, submissions=submission) 21 | 22 | print(f"{submission.status=}") 23 | print(f"{submission.stdout=}") 24 | 25 | 26 | def main(): 27 | run_example(judge0.RapidJudge0CE, 100) 28 | run_example(judge0.RapidJudge0ExtraCE, 25) 29 | 30 | 31 | if __name__ == "__main__": 32 | main() 33 | -------------------------------------------------------------------------------- /docs/Makefile: -------------------------------------------------------------------------------- 1 | # Minimal makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line, and also 5 | # from the environment for the first two. 6 | SPHINXOPTS ?= 7 | SPHINXBUILD ?= uv run sphinx-build 8 | SOURCEDIR = source 9 | BUILDDIR = build 10 | 11 | # Put it first so that "make" without argument is like "make help". 12 | help: 13 | @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 14 | 15 | .PHONY: help Makefile 16 | 17 | # Catch-all target: route all unknown targets to Sphinx using the new 18 | # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). 19 | %: Makefile 20 | @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 21 | 22 | livehtml: 23 | uv run sphinx-autobuild -b dirhtml --pre-build "rm -rf $(BUILDDIR)" "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) -------------------------------------------------------------------------------- /.env.template: -------------------------------------------------------------------------------- 1 | # Rapid API Key. Get yours at https://rapidapi.com/developer/apps 2 | # Subscribe here: https://rapidapi.com/organization/judge0 3 | JUDGE0_RAPID_API_KEY= 4 | 5 | # ATD API Key. Get yours at https://www.allthingsdev.co/account/apps 6 | # Subscribe here: https://www.allthingsdev.co/publisher/profile/Herman%20Zvonimir%20Došilović 7 | JUDGE0_ATD_API_KEY= 8 | 9 | # Auth headers for Judge0 Cloud. 10 | # Contact us to get your credentials. 11 | JUDGE0_CLOUD_CE_AUTH_HEADERS= 12 | JUDGE0_CLOUD_EXTRA_CE_AUTH_HEADERS= 13 | 14 | # Endpoint and auth headers for custom Judge0 CE instance. 15 | # Use to connect to your own Judge0 CE server. 16 | JUDGE0_CE_ENDPOINT= 17 | JUDGE0_CE_AUTH_HEADERS= # Use "{}" for no auth headers. 18 | 19 | # Endpoint and auth headers for custom Judge0 Extra CE instance. 20 | # Use to connect to your own Judge0 Extra CE server. 21 | JUDGE0_EXTRA_CE_ENDPOINT= 22 | JUDGE0_EXTRA_CE_AUTH_HEADERS= # Use "{}" for no auth headers. 23 | -------------------------------------------------------------------------------- /docs/make.bat: -------------------------------------------------------------------------------- 1 | @ECHO OFF 2 | 3 | pushd %~dp0 4 | 5 | REM Command file for Sphinx documentation 6 | 7 | if "%SPHINXBUILD%" == "" ( 8 | set SPHINXBUILD=sphinx-build 9 | ) 10 | set SOURCEDIR=source 11 | set BUILDDIR=build 12 | 13 | %SPHINXBUILD% >NUL 2>NUL 14 | if errorlevel 9009 ( 15 | echo. 16 | echo.The 'sphinx-build' command was not found. Make sure you have Sphinx 17 | echo.installed, then set the SPHINXBUILD environment variable to point 18 | echo.to the full path of the 'sphinx-build' executable. Alternatively you 19 | echo.may add the Sphinx directory to PATH. 20 | echo. 21 | echo.If you don't have Sphinx installed, grab it from 22 | echo.https://www.sphinx-doc.org/ 23 | exit /b 1 24 | ) 25 | 26 | if "%1" == "" goto help 27 | 28 | %SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% 29 | goto end 30 | 31 | :help 32 | %SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% 33 | 34 | :end 35 | popd 36 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Desktop (please complete the following information):** 27 | - OS: [e.g. iOS] 28 | - Browser [e.g. chrome, safari] 29 | - Version [e.g. 22] 30 | 31 | **Smartphone (please complete the following information):** 32 | - Device: [e.g. iPhone6] 33 | - OS: [e.g. iOS8.1] 34 | - Browser [e.g. stock browser, safari] 35 | - Version [e.g. 22] 36 | 37 | **Additional context** 38 | Add any other context about the problem here. 39 | -------------------------------------------------------------------------------- /examples/1000_http_callback_aka_webhook/README.md: -------------------------------------------------------------------------------- 1 | # HTTP Callbacks (a.k.a. Webhooks) 2 | ![demo.gif](.github/demo.gif) 3 | 4 | ```bash 5 | pip install -r requirements.txt 6 | ./main.py 7 | ``` 8 | 9 | In this example we are creating a HTTP server that has two routes: 10 | 1. `GET /` - triggers the creation of submission on Judge0 11 | 2. `PUT /callback` - called by Judge0 once the submission is done 12 | 13 | When you run the HTTP server you should visit `http://localhost:8000` and that will create a new submission on Judge0. This submission will have a `callback_url` attribute set which Judge0 will call `PUT` HTTP verb once the submission is done. 14 | 15 | For this to work the `callback_url` must be publicly accessible, so in this example we are using a free service https://localhost.run that allows us to expose your local HTTP server and make it available on the internet and accessible by Judge0. 16 | 17 | Once the submission is done Judge0 will call our second route `PUT /callback` and in your terminal you should see the result. 18 | -------------------------------------------------------------------------------- /docs/source/api/types.rst: -------------------------------------------------------------------------------- 1 | Types Module 2 | ============ 3 | 4 | Types 5 | ----- 6 | 7 | .. autoclass:: judge0.base_types.Config 8 | :members: 9 | :member-order: bysource 10 | 11 | .. autoclass:: judge0.base_types.Encodable 12 | :members: 13 | 14 | .. autoclass:: judge0.base_types.Flavor 15 | :members: 16 | :member-order: bysource 17 | 18 | .. autoclass:: judge0.base_types.Language 19 | :members: 20 | :member-order: bysource 21 | 22 | .. autoclass:: judge0.base_types.LanguageAlias 23 | :members: 24 | :member-order: bysource 25 | 26 | .. autoclass:: judge0.base_types.Status 27 | :members: 28 | :member-order: bysource 29 | 30 | .. autoclass:: judge0.base_types.TestCase 31 | :members: 32 | :member-order: bysource 33 | 34 | Type aliases 35 | ------------ 36 | 37 | .. autoclass:: judge0.base_types.Iterable 38 | :members: 39 | :member-order: bysource 40 | 41 | .. autoclass:: judge0.base_types.TestCaseType 42 | :members: 43 | :member-order: bysource 44 | 45 | .. autoclass:: judge0.base_types.TestCases 46 | :members: 47 | :member-order: bysource -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 Judge0 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /examples/atd_submissions.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | import judge0 4 | 5 | from dotenv import load_dotenv 6 | 7 | load_dotenv() 8 | 9 | api_key = os.getenv("JUDGE0_ATD_API_KEY") 10 | 11 | 12 | def run_example(client_class, lang_id_python, lang_id_c): 13 | client = client_class(api_key=api_key) 14 | submission1 = judge0.Submission( 15 | source_code="print('Hello Judge0')", 16 | language=lang_id_python, 17 | expected_output="Hello Judge0", 18 | ) 19 | submission2 = judge0.Submission( 20 | source_code='#include \n\nint main() {\n printf("Hello World!");\n return 0;\n}', 21 | language=lang_id_c, 22 | expected_output="Hello World!", 23 | ) 24 | 25 | submissions = [submission1, submission2] 26 | judge0.execute(client=client, submissions=submissions) 27 | 28 | for submission in submissions: 29 | print(f"{submission.status=}") 30 | print(f"{submission.stdout=}") 31 | 32 | 33 | def main(): 34 | run_example(judge0.ATDJudge0CE, 100, 103) 35 | run_example(judge0.ATDJudge0ExtraCE, 25, 1) 36 | 37 | 38 | if __name__ == "__main__": 39 | main() 40 | -------------------------------------------------------------------------------- /examples/rapid_submissions.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | import judge0 4 | 5 | from dotenv import load_dotenv 6 | 7 | load_dotenv() 8 | 9 | api_key = os.getenv("JUDGE0_RAPID_API_KEY") 10 | 11 | 12 | def run_example(client_class, lang_id_python, lang_id_c): 13 | client = client_class(api_key=api_key) 14 | submission1 = judge0.Submission( 15 | source_code="print('Hello Judge0')", 16 | language=lang_id_python, 17 | expected_output="Hello Judge0", 18 | ) 19 | submission2 = judge0.Submission( 20 | source_code='#include \n\nint main() {\n printf("Hello World!");\n return 0;\n}', 21 | language=lang_id_c, 22 | expected_output="Hello World!", 23 | ) 24 | 25 | submissions = [submission1, submission2] 26 | judge0.execute(client=client, submissions=submissions) 27 | 28 | for submission in submissions: 29 | print(f"{submission.status=}") 30 | print(f"{submission.stdout=}") 31 | 32 | 33 | def main(): 34 | run_example(judge0.RapidJudge0CE, 100, 50) 35 | run_example(judge0.RapidJudge0ExtraCE, 25, 1) 36 | 37 | 38 | if __name__ == "__main__": 39 | main() 40 | -------------------------------------------------------------------------------- /.github/workflows/publish.yml: -------------------------------------------------------------------------------- 1 | name: Publish to PyPI 2 | 3 | on: 4 | workflow_dispatch: 5 | inputs: 6 | repository: 7 | description: "Target repository (pypi or test)" 8 | required: true 9 | default: "pypi" 10 | 11 | jobs: 12 | publish: 13 | runs-on: ubuntu-24.04 14 | 15 | steps: 16 | - uses: actions/checkout@v4 17 | 18 | - name: Set up Python 19 | uses: actions/setup-python@v5 20 | with: 21 | python-version: "3.10" 22 | 23 | - name: Install uv 24 | run: curl -LsSf https://astral.sh/uv/0.9.8/install.sh | sh 25 | 26 | - name: Build package with uv 27 | run: uv build 28 | 29 | - name: Publish to Test PyPI 30 | if: github.event.inputs.repository == 'test' 31 | env: 32 | UV_PUBLISH_TOKEN: ${{ secrets.TEST_PYPI_TOKEN }} 33 | run: uv publish --token $UV_PUBLISH_TOKEN --publish-url https://test.pypi.org/legacy/ 34 | 35 | - name: Publish to Regular PyPI 36 | if: github.event.inputs.repository == 'pypi' 37 | env: 38 | UV_PUBLISH_TOKEN: ${{ secrets.PYPI_TOKEN }} 39 | run: uv publish --token $UV_PUBLISH_TOKEN 40 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: Test judge0-python 2 | 3 | on: 4 | workflow_dispatch: 5 | push: 6 | branches: ["master"] 7 | paths: ["src/**", "tests/**"] 8 | pull_request: 9 | branches: ["master"] 10 | paths: ["src/**", "tests/**"] 11 | 12 | permissions: 13 | contents: read 14 | 15 | jobs: 16 | test: 17 | runs-on: ubuntu-24.04 18 | 19 | steps: 20 | - uses: actions/checkout@v4 21 | - name: Set up Python 3.10 22 | uses: actions/setup-python@v3 23 | with: 24 | python-version: "3.10" 25 | - name: Install uv and project 26 | run: | 27 | curl -LsSf https://astral.sh/uv/0.9.8/install.sh | sh 28 | uv sync --group test 29 | - name: Test with pytest 30 | env: # Add necessary api keys as env variables. 31 | JUDGE0_ATD_API_KEY: ${{ secrets.JUDGE0_ATD_API_KEY }} 32 | JUDGE0_RAPID_API_KEY: ${{ secrets.JUDGE0_RAPID_API_KEY }} 33 | JUDGE0_CE_AUTH_HEADERS: ${{ secrets.JUDGE0_CE_AUTH_HEADERS }} 34 | JUDGE0_EXTRA_CE_AUTH_HEADERS: ${{ secrets.JUDGE0_EXTRA_CE_AUTH_HEADERS }} 35 | JUDGE0_CE_ENDPOINT: ${{ secrets.JUDGE0_CE_ENDPOINT }} 36 | JUDGE0_EXTRA_CE_ENDPOINT: ${{ secrets.JUDGE0_EXTRA_CE_ENDPOINT }} 37 | JUDGE0_CLOUD_CE_AUTH_HEADERS: ${{ secrets.JUDGE0_CLOUD_CE_AUTH_HEADERS }} 38 | JUDGE0_CLOUD_EXTRA_CE_AUTH_HEADERS: ${{ secrets.JUDGE0_CLOUD_EXTRA_CE_AUTH_HEADERS }} 39 | run: uv run pytest tests 40 | -------------------------------------------------------------------------------- /examples/0005_filesystem.py: -------------------------------------------------------------------------------- 1 | import judge0 2 | from judge0 import File, Filesystem 3 | 4 | print("Subexample 1") 5 | result = judge0.run(source_code="print('hello, world')") 6 | fs = Filesystem(content=result.post_execution_filesystem) 7 | for f in fs: 8 | print(f.name) 9 | print(f) 10 | print() 11 | 12 | 13 | print("Subexample 2") 14 | fs = Filesystem(content=File(name="my_file.txt", content="hello, world")) 15 | result = judge0.run( 16 | source_code="print(open('my_file.txt').read())", additional_files=fs 17 | ) 18 | print(result.stdout) 19 | for f in Filesystem(content=result.post_execution_filesystem): 20 | print(f.name) 21 | print(f) 22 | print() 23 | 24 | 25 | print("Subexample 3") 26 | fs = Filesystem(content=File(name="my_file.txt", content="hello, world")) 27 | result = judge0.run( 28 | source_code="print(open('my_file.txt').read())", additional_files=fs 29 | ) 30 | print(result.stdout) 31 | for f in result.post_execution_filesystem: 32 | print(f.name) 33 | print(f) 34 | print() 35 | 36 | print("Subexample 4") 37 | fs = Filesystem( 38 | content=[ 39 | File(name="my_file.txt", content="hello, world"), 40 | File(name="./dir1/dir2/dir3/my_file2.txt", content="hello, world2"), 41 | ] 42 | ) 43 | result = judge0.run(source_code="find .", additional_files=fs, language=46) 44 | print(result.stdout) 45 | for f in Filesystem(content=result.post_execution_filesystem): 46 | print(f.name) 47 | print(f) 48 | print() 49 | -------------------------------------------------------------------------------- /tests/test_clients.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | DEFAULT_CLIENTS = ( 4 | "atd_ce_client", 5 | "atd_extra_ce_client", 6 | "rapid_ce_client", 7 | "rapid_extra_ce_client", 8 | "judge0_cloud_ce_client", 9 | "judge0_cloud_extra_ce_client", 10 | ) 11 | 12 | 13 | @pytest.mark.parametrize("client", DEFAULT_CLIENTS) 14 | def test_get_about(client, request): 15 | client = request.getfixturevalue(client) 16 | client.get_about() 17 | 18 | 19 | @pytest.mark.parametrize("client", DEFAULT_CLIENTS) 20 | def test_get_config_info(client, request): 21 | client = request.getfixturevalue(client) 22 | client.get_config_info() 23 | 24 | 25 | @pytest.mark.parametrize("client", DEFAULT_CLIENTS) 26 | def test_get_languages(client, request): 27 | client = request.getfixturevalue(client) 28 | client.get_languages() 29 | 30 | 31 | @pytest.mark.parametrize("client", DEFAULT_CLIENTS) 32 | def test_get_statuses(client, request): 33 | client = request.getfixturevalue(client) 34 | client.get_statuses() 35 | 36 | 37 | @pytest.mark.parametrize("client", DEFAULT_CLIENTS) 38 | def test_is_language_supported_multi_file_submission(client, request): 39 | client = request.getfixturevalue(client) 40 | assert client.is_language_supported(89) 41 | 42 | 43 | @pytest.mark.parametrize("client", DEFAULT_CLIENTS) 44 | def test_is_language_supported_non_valid_lang_id(client, request): 45 | client = request.getfixturevalue(client) 46 | assert not client.is_language_supported(-1) 47 | -------------------------------------------------------------------------------- /src/judge0/common.py: -------------------------------------------------------------------------------- 1 | from base64 import b64decode, b64encode 2 | from itertools import islice 3 | from typing import Union 4 | 5 | from judge0.base_types import Encodable 6 | 7 | 8 | def encode(content: Union[bytes, str, Encodable]) -> str: 9 | """Encode content to base64 string.""" 10 | if isinstance(content, bytes): 11 | return b64encode(content).decode() 12 | if isinstance(content, str): 13 | return b64encode(content.encode()).decode() 14 | if isinstance(content, Encodable): 15 | return b64encode(content.encode()).decode() 16 | raise ValueError(f"Unsupported type. Expected bytes or str, got {type(content)}!") 17 | 18 | 19 | def decode(content: Union[bytes, str]) -> str: 20 | """Decode base64 encoded content.""" 21 | if isinstance(content, bytes): 22 | return b64decode( 23 | content.decode(errors="backslashreplace"), validate=True 24 | ).decode(errors="backslashreplace") 25 | if isinstance(content, str): 26 | return b64decode(content.encode(), validate=True).decode( 27 | errors="backslashreplace" 28 | ) 29 | raise ValueError(f"Unsupported type. Expected bytes or str, got {type(content)}!") 30 | 31 | 32 | def batched(iterable, n): 33 | """Iterate over an iterable in batches of a specified size. 34 | 35 | Adapted from https://docs.python.org/3/library/itertools.html#itertools.batched. 36 | """ 37 | if n < 1: 38 | raise ValueError("n must be at least one") 39 | iterator = iter(iterable) 40 | while True: 41 | batch = tuple(islice(iterator, n)) 42 | if not batch: 43 | break 44 | yield batch 45 | -------------------------------------------------------------------------------- /examples/0005_test_cases.py: -------------------------------------------------------------------------------- 1 | import judge0 2 | 3 | print("Subexample 1") 4 | result = judge0.run( 5 | source_code="print(f'Hello, {input()}')", 6 | test_cases=judge0.TestCase("Herman", "Hello, Herman"), 7 | ) 8 | 9 | print(result.status) 10 | 11 | 12 | print("Subexample 2") 13 | results = judge0.run( 14 | source_code="print(f'Hello, {input()}')", 15 | test_cases=[ 16 | judge0.TestCase("Herman", "Hello, Herman"), 17 | judge0.TestCase("Filip", "Hello, Filip"), 18 | ], 19 | ) 20 | 21 | for result in results: 22 | print(result.status) 23 | 24 | 25 | print("Subexample 3") 26 | submission = judge0.Submission(source_code="print(f'Hello, {input()}')") 27 | result = judge0.run( 28 | submissions=submission, 29 | test_cases=judge0.TestCase("Herman", "Hello, Herman"), 30 | ) 31 | 32 | print(result.status) 33 | 34 | 35 | print("Subexample 4") 36 | submissions = [ 37 | judge0.Submission(source_code="print(f'Hello, {input()}')"), 38 | judge0.Submission(source_code="print(f'Bok, {input()}')"), 39 | ] 40 | results = judge0.run( 41 | submissions=submissions, 42 | test_cases=judge0.TestCase("Herman", "Hello, Herman"), 43 | ) 44 | 45 | for result in results: 46 | print(result.status) 47 | 48 | 49 | print("Subexample 5") 50 | submissions = [ 51 | judge0.Submission(source_code="print(f'Hello, {input()}')"), 52 | judge0.Submission(source_code="print(f'Bok, {input()}')"), 53 | ] 54 | results = judge0.run( 55 | submissions=submissions, 56 | test_cases=[ 57 | judge0.TestCase("Herman", "Hello, Herman"), 58 | judge0.TestCase("Filip", "Hello, Filip"), 59 | ], 60 | ) 61 | 62 | for result in results: 63 | print(result.status) 64 | -------------------------------------------------------------------------------- /docs/source/api/clients.rst: -------------------------------------------------------------------------------- 1 | Clients Module 2 | ============== 3 | 4 | 5 | .. autoclass:: judge0.clients.Client 6 | :exclude-members: API_KEY_ENV 7 | 8 | .. autoclass:: judge0.clients.ATD 9 | :show-inheritance: 10 | 11 | .. autoclass:: judge0.clients.ATDJudge0CE 12 | :show-inheritance: 13 | :exclude-members: DEFAULT_ENDPOINT, DEFAULT_HOST, HOME_URL, DEFAULT_ABOUT_ENDPOINT, 14 | DEFAULT_CONFIG_INFO_ENDPOINT, DEFAULT_LANGUAGE_ENDPOINT, DEFAULT_LANGUAGES_ENDPOINT, 15 | DEFAULT_STATUSES_ENDPOINT, DEFAULT_CREATE_SUBMISSION_ENDPOINT, DEFAULT_GET_SUBMISSION_ENDPOINT, 16 | DEFAULT_CREATE_SUBMISSIONS_ENDPOINT, DEFAULT_GET_SUBMISSIONS_ENDPOINT, get_about, 17 | get_config_info, get_language, get_languages, get_statuses, create_submission, get_submission, 18 | create_submissions, get_submissions 19 | 20 | .. autoclass:: judge0.clients.ATDJudge0ExtraCE 21 | :show-inheritance: 22 | :exclude-members: DEFAULT_ENDPOINT, DEFAULT_HOST, HOME_URL, DEFAULT_ABOUT_ENDPOINT, 23 | DEFAULT_CONFIG_INFO_ENDPOINT, DEFAULT_LANGUAGE_ENDPOINT, DEFAULT_LANGUAGES_ENDPOINT, 24 | DEFAULT_STATUSES_ENDPOINT, DEFAULT_CREATE_SUBMISSION_ENDPOINT, DEFAULT_GET_SUBMISSION_ENDPOINT, 25 | DEFAULT_CREATE_SUBMISSIONS_ENDPOINT, DEFAULT_GET_SUBMISSIONS_ENDPOINT, get_about, 26 | get_config_info, get_language, get_languages, get_statuses, create_submission, get_submission, 27 | create_submissions, get_submissions 28 | 29 | 30 | .. autoclass:: judge0.clients.Rapid 31 | :show-inheritance: 32 | 33 | .. autoclass:: judge0.clients.RapidJudge0CE 34 | :show-inheritance: 35 | 36 | .. autoclass:: judge0.clients.RapidJudge0ExtraCE 37 | :show-inheritance: 38 | -------------------------------------------------------------------------------- /src/judge0/utils.py: -------------------------------------------------------------------------------- 1 | """Module containing different utility functions for Judge0 Python SDK.""" 2 | 3 | from functools import wraps 4 | from http import HTTPStatus 5 | 6 | from requests import HTTPError 7 | 8 | from .errors import PreviewClientLimitError 9 | 10 | 11 | def is_http_too_many_requests_error(exception: Exception) -> bool: 12 | return ( 13 | isinstance(exception, HTTPError) 14 | and exception.response is not None 15 | and exception.response.status_code == HTTPStatus.TOO_MANY_REQUESTS 16 | ) 17 | 18 | 19 | def handle_too_many_requests_error_for_preview_client(func): 20 | @wraps(func) 21 | def wrapper(*args, **kwargs): 22 | try: 23 | return func(*args, **kwargs) 24 | except HTTPError as err: 25 | if is_http_too_many_requests_error(exception=err): 26 | # If the raised exception is inside the one of the Judge0 Cloud clients 27 | # let's check if we are dealing with the implicit client. 28 | if args: 29 | instance = args[0] 30 | class_name = instance.__class__.__name__ 31 | # Check if we are using a preview version of the client. 32 | if ( 33 | class_name in ("Judge0CloudCE", "Judge0CloudExtraCE") 34 | and instance.api_key is None 35 | ): 36 | raise PreviewClientLimitError( 37 | "You are using a preview version of a client and " 38 | f"you've hit a rate limit on it. Visit {instance.HOME_URL} " 39 | "to get your authentication credentials." 40 | ) from err 41 | else: 42 | raise err from None 43 | else: 44 | raise err from None 45 | except Exception as err: 46 | raise err from None 47 | 48 | return wrapper 49 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [project] 2 | name = "judge0" 3 | version = "0.1.0.dev0" 4 | description = "The official Python SDK for Judge0." 5 | readme = "README.md" 6 | requires-python = ">=3.10" 7 | authors = [{ name = "Judge0", email = "contact@judge0.com" }] 8 | classifiers = [ 9 | "Intended Audience :: Developers", 10 | 11 | "License :: OSI Approved :: MIT License", 12 | 13 | "Programming Language :: Python :: 3", 14 | "Programming Language :: Python :: 3.10", 15 | "Programming Language :: Python :: 3.11", 16 | "Programming Language :: Python :: 3.12", 17 | 18 | "Operating System :: OS Independent", 19 | "Operating System :: POSIX", 20 | "Operating System :: MacOS", 21 | "Operating System :: POSIX :: Linux", 22 | "Operating System :: Microsoft :: Windows", 23 | 24 | "Topic :: Software Development :: Libraries :: Python Modules", 25 | "Typing :: Typed", 26 | ] 27 | dependencies = ["requests>=2.28.0,<3.0.0", "pydantic>=2.0.0,<3.0.0"] 28 | 29 | [build-system] 30 | requires = ["setuptools>=70.0"] 31 | build-backend = "setuptools.build_meta" 32 | 33 | [project.urls] 34 | Homepage = "https://github.com/judge0/judge0-python" 35 | Repository = "https://github.com/judge0/judge0-python.git" 36 | Issues = "https://github.com/judge0/judge0-python/issues" 37 | 38 | [dependency-groups] 39 | test = [ 40 | "ufmt==2.7.3", 41 | "pytest==8.3.3", 42 | "python-dotenv==1.0.1", 43 | "pytest-cov==6.0.0", 44 | "flake8-docstrings==1.7.0", 45 | ] 46 | docs = [ 47 | "sphinx==8.1.3", 48 | "sphinxawesome-theme==5.3.2", 49 | "sphinx-autodoc-typehints==2.3.0", 50 | "sphinx-multiversion==0.2.4", 51 | "sphinx-autobuild>=2024.10.3", 52 | ] 53 | dev = [{ include-group = "test" }, { include-group = "docs" }] 54 | 55 | [tool.flake8] 56 | extend-ignore = [ 57 | 'D100', 58 | 'D101', 59 | 'D102', 60 | 'D103', 61 | 'D104', 62 | 'D105', 63 | 'D107', 64 | 'D205', 65 | "D209", 66 | 'D400', 67 | 'F821', 68 | ] 69 | docstring-convention = "numpy" 70 | max-line-length = 88 71 | 72 | [tool.pytest.ini_options] 73 | addopts = "-vv" 74 | -------------------------------------------------------------------------------- /docs/source/contributors_guide/release_notes.rst: -------------------------------------------------------------------------------- 1 | How to create a release 2 | ======================= 3 | 4 | Creating a release is a simple process that involves a few steps: 5 | 6 | #. **Prepare the release**: 7 | #. Create a separate branch for the release. Name the branch ``release-x.y.z`` 8 | where ``x.y.z`` is the version number. 9 | #. Update the version number in ``judge0/__init__.py``. 10 | #. Update the version number in ``judge0/pyproject.toml``. 11 | #. Sync the branch with any changes from the master branch. 12 | #. Create a pull request for the release branch. Make sure that all tests pass. 13 | #. Merge the pull request. 14 | #. Pull the changes to your local repository and tag the commit (``git tag vX.Y.Z``) with the version number. 15 | #. Push the tags to the remote repository (``git push origin master --tags``). 16 | #. **Create release (notes) on GitHub**. 17 | #. Go to the `releases page `_ on GitHub. 18 | #. Release title should be ``Judge0 Python SDK vX.Y.Z``. 19 | #. Release notes should include a changes from the previous release to the newest release. 20 | #. Use the `template `_ from the repo to organize the changes. 21 | #. Click the `Generate release notes` button and use only `Full Changelog` section. 22 | #. Create the release. ("Set as a pre-release" should NOT be checked.) 23 | #. **Release on PyPI**: 24 | #. Use the `GitHub Actions workflow `_ to create a release on PyPI. 25 | #. Select `Run workflow` and as `Target repository` select `pypi`. 26 | #. Click the `Run workflow` button. 27 | 28 | After the release is successfully published on PyPI, create a new pull request 29 | that updates the working version in ``judge0/__init__.py`` and ``judge0/pyproject.toml`` 30 | to the minor version. Merge the pull request and you're done! For example, if the 31 | new release was ``1.2.2``, the working version should be updated to ``1.3.0.dev0``. 32 | 33 | You've successfully created a release! Congratulations! 🎉 34 | -------------------------------------------------------------------------------- /docs/source/index.rst: -------------------------------------------------------------------------------- 1 | ================= 2 | Judge0 Python SDK 3 | ================= 4 | 5 | Getting Started 6 | =============== 7 | 8 | You can run minimal Hello World example in three easy steps: 9 | 10 | 1. Install Judge0 Python SDK: 11 | 12 | .. code-block:: bash 13 | 14 | pip install judge0 15 | 16 | 2. Create a minimal script: 17 | 18 | .. code-block:: python 19 | 20 | import judge0 21 | 22 | submission = judge.run(source_code="print('Hello Judge0!')") 23 | print(submission.stdout) 24 | 25 | 3. Run the script. 26 | 27 | Want to learn more 28 | ================== 29 | 30 | To learn what is happening behind the scenes and how to best use Judge0 Python 31 | SDK to facilitate the development of your own product see In Depth guide and 32 | `examples `_. 33 | 34 | Getting Involved 35 | ================ 36 | 37 | Getting involved in any open-source project is simple and rewarding, with 38 | multiple ways to contribute to its growth and success. You can help by: 39 | 40 | 1. `reporting bugs `_ by 41 | creating a detailed issue describing the problem, along with any relevant code or 42 | steps to reproduce it, so it can be addressed effectively, 43 | 2. creating a `pull request `_ for 44 | an existing issue; we welcome improvements, fixes, and new features that align 45 | with the project's goals, and 46 | 3. you can show support by starring the `repository `_, 47 | letting us know that we’re doing a good job and helping us gain visibility within 48 | the open-source community. 49 | 50 | Every contribution, big or small, is valuable! 51 | 52 | .. toctree:: 53 | :caption: API 54 | :glob: 55 | :titlesonly: 56 | :hidden: 57 | 58 | api/api 59 | api/clients 60 | api/errors 61 | api/filesystem 62 | api/retry 63 | api/submission 64 | api/types 65 | 66 | 67 | .. toctree:: 68 | :caption: In Depth 69 | :glob: 70 | :titlesonly: 71 | :hidden: 72 | 73 | in_depth/overview 74 | in_depth/client_resolution 75 | 76 | 77 | .. toctree:: 78 | :caption: Getting Involved 79 | :glob: 80 | :titlesonly: 81 | :hidden: 82 | 83 | contributors_guide/contributing 84 | contributors_guide/release_notes 85 | -------------------------------------------------------------------------------- /.github/workflows/docs.yml: -------------------------------------------------------------------------------- 1 | name: "Sphinx: Render docs" 2 | 3 | on: 4 | workflow_dispatch: 5 | push: 6 | branches: ["master"] 7 | 8 | jobs: 9 | build: 10 | runs-on: ubuntu-24.04 11 | permissions: 12 | contents: write 13 | steps: 14 | - uses: actions/checkout@v4 15 | with: 16 | persist-credentials: false 17 | fetch-depth: 0 # Fetch the full history 18 | ref: ${{ github.ref }} # Check out the current branch or tag 19 | 20 | - name: Fetch version tags only (vx.y.z) 21 | run: | 22 | TAGS=$(git ls-remote --tags origin 'v*[0-9].[0-9].[0-9]' | sed 's/.*refs\/tags\///' | grep -E '^v[0-9]+\.[0-9]+\.[0-9]+$') 23 | for tag in $TAGS; do 24 | git fetch origin "refs/tags/$tag:refs/tags/$tag" --no-recurse-submodules 25 | done 26 | 27 | - name: Set up Python 28 | uses: actions/setup-python@v4 29 | with: 30 | python-version: "3.10" 31 | 32 | - name: Install uv and dependencies 33 | run: | 34 | curl -LsSf https://astral.sh/uv/0.9.8/install.sh | sh 35 | uv sync --group docs 36 | 37 | - name: Build documentation 38 | run: uv run sphinx-multiversion docs/source docs/build/html --keep-going --no-color 39 | 40 | - name: Get the latest stable tag 41 | run: | 42 | # Fetch all tags 43 | git fetch --tags 44 | # Get the latest stable tag 45 | latest_tag=$(git tag --sort=-creatordate | grep -E '^v[0-9]+\.[0-9]+\.[0-9]+$' | head -n 1) 46 | echo "LATEST_RELEASE=$latest_tag" >> $GITHUB_ENV 47 | 48 | - name: Generate index.html for judge0.github.io/judge0-python. 49 | run: | 50 | echo ' 51 | 52 | 53 | 54 | 55 | ' > docs/build/html/index.html 56 | env: 57 | latest_release: ${{ env.LATEST_RELEASE }} 58 | 59 | - name: Generate CNAME file 60 | run: echo "python.docs.judge0.com" > docs/build/html/CNAME 61 | 62 | - name: Upload artifacts 63 | uses: actions/upload-artifact@v4 64 | with: 65 | name: html-docs 66 | path: docs/build/html/ 67 | 68 | - name: Deploy 69 | uses: peaceiris/actions-gh-pages@v3 70 | if: github.ref == 'refs/heads/master' 71 | with: 72 | github_token: ${{ secrets.GITHUB_TOKEN }} 73 | publish_dir: docs/build/html 74 | -------------------------------------------------------------------------------- /examples/1000_http_callback_aka_webhook/main.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | import asyncio 3 | 4 | import judge0 5 | 6 | import uvicorn 7 | from fastapi import Depends, FastAPI 8 | 9 | 10 | class AppContext: 11 | def __init__(self): 12 | self.public_url = "" 13 | 14 | 15 | LOCAL_SERVER_PORT = 8000 16 | 17 | app = FastAPI() 18 | app_context = AppContext() 19 | 20 | 21 | def get_app_context(): 22 | return app_context 23 | 24 | 25 | @app.get("/") 26 | async def root(app_context=Depends(get_app_context)): 27 | if not app_context.public_url: 28 | return { 29 | "message": "Public URL is not available yet. Try again after a few seconds." 30 | } 31 | 32 | submission = judge0.Submission( 33 | source_code="print('Hello, World!')", 34 | language_id=judge0.PYTHON, 35 | callback_url=f"{app_context.public_url}/callback", 36 | ) 37 | 38 | return judge0.async_execute(submissions=submission) 39 | 40 | 41 | @app.put("/callback") 42 | async def callback(response: judge0.Submission): 43 | print(f"Received: {response}") 44 | 45 | 46 | # We are using free service from https://localhost.run to get a public URL for 47 | # our local server. This approach is not recommended for production use. It is 48 | # only for demonstration purposes since domain names change regularly and there 49 | # is a speed limit for the free service. 50 | async def run_ssh_tunnel(): 51 | app_context = get_app_context() 52 | 53 | command = [ 54 | "ssh", 55 | "-o", 56 | "StrictHostKeyChecking=no", 57 | "-o", 58 | "ServerAliveInterval=30", 59 | "-R", 60 | f"80:localhost:{LOCAL_SERVER_PORT}", 61 | "localhost.run", 62 | ] 63 | 64 | process = await asyncio.create_subprocess_exec( 65 | *command, 66 | stdout=asyncio.subprocess.PIPE, 67 | stderr=asyncio.subprocess.STDOUT, 68 | ) 69 | 70 | while True: 71 | line = await process.stdout.readline() 72 | if not line: 73 | break 74 | 75 | decoded_line = line.decode().strip() 76 | if decoded_line.endswith(".lhr.life"): 77 | app_context.public_url = decoded_line.split()[-1].strip() 78 | 79 | await process.wait() 80 | 81 | 82 | async def run_server(): 83 | config = uvicorn.Config( 84 | app, 85 | host="127.0.0.1", 86 | port=LOCAL_SERVER_PORT, 87 | workers=5, 88 | loop="asyncio", 89 | ) 90 | server = uvicorn.Server(config) 91 | await server.serve() 92 | 93 | 94 | async def main(): 95 | await asyncio.gather(run_ssh_tunnel(), run_server()) 96 | 97 | 98 | if __name__ == "__main__": 99 | asyncio.run(main()) 100 | -------------------------------------------------------------------------------- /tests/test_api.py: -------------------------------------------------------------------------------- 1 | import judge0 2 | import pytest 3 | 4 | from judge0 import Flavor, LanguageAlias, Submission 5 | from judge0.api import _resolve_client 6 | 7 | DEFAULT_CLIENTS = ( 8 | "atd_ce_client", 9 | "atd_extra_ce_client", 10 | "rapid_ce_client", 11 | "rapid_extra_ce_client", 12 | "judge0_cloud_ce_client", 13 | "judge0_cloud_extra_ce_client", 14 | ) 15 | 16 | 17 | @pytest.mark.parametrize("client", DEFAULT_CLIENTS) 18 | def test_resolve_client_with_explicit_client(client, request): 19 | client = request.getfixturevalue(client) 20 | assert _resolve_client(client) is client 21 | 22 | 23 | @pytest.mark.parametrize( 24 | "flavor,expected_client", 25 | [ 26 | [ 27 | Flavor.CE, 28 | "JUDGE0_IMPLICIT_CE_CLIENT", 29 | ], 30 | [ 31 | Flavor.EXTRA_CE, 32 | "JUDGE0_IMPLICIT_EXTRA_CE_CLIENT", 33 | ], 34 | ], 35 | ) 36 | def test_resolve_client_with_flavor( 37 | flavor, 38 | expected_client, 39 | ): 40 | # We have to use getattr since both implicit clients are initially None. 41 | assert _resolve_client(client=flavor) is getattr(judge0, expected_client) 42 | 43 | 44 | @pytest.mark.parametrize( 45 | "submissions", 46 | [ 47 | [], 48 | None, 49 | ], 50 | ) 51 | def test_resolve_client_empty_submissions_argument(submissions): 52 | with pytest.raises(ValueError): 53 | _resolve_client(submissions=submissions) 54 | 55 | 56 | def test_resolve_client_no_common_client_for_submissions(): 57 | cpp_submission = Submission( 58 | source_code="", # source code is not important in this test 59 | language=LanguageAlias.CPP_GCC, 60 | ) 61 | 62 | py_submission = Submission( 63 | source_code="", # source code is not important in this test 64 | language=LanguageAlias.PYTHON_FOR_ML, 65 | ) 66 | 67 | submissions = [cpp_submission, py_submission] 68 | 69 | with pytest.raises(RuntimeError): 70 | _resolve_client(submissions=submissions) 71 | 72 | 73 | def test_resolve_client_common_ce_client(): 74 | cpp_submission = Submission( 75 | source_code="", # source code is not important in this test 76 | language=LanguageAlias.CPP_GCC, 77 | ) 78 | 79 | py_submission = Submission( 80 | source_code="", # source code is not important in this test 81 | language=LanguageAlias.PYTHON, 82 | ) 83 | 84 | submissions = [cpp_submission, py_submission] 85 | 86 | assert _resolve_client(submissions=submissions) is judge0.JUDGE0_IMPLICIT_CE_CLIENT 87 | 88 | 89 | def test_resolve_client_common_extra_ce_client(): 90 | cpp_submission = Submission( 91 | source_code="", # source code is not important in this test 92 | language=LanguageAlias.CPP_CLANG, 93 | ) 94 | 95 | py_submission = Submission( 96 | source_code="", # source code is not important in this test 97 | language=LanguageAlias.PYTHON_FOR_ML, 98 | ) 99 | 100 | submissions = [cpp_submission, py_submission] 101 | 102 | assert ( 103 | _resolve_client(submissions=submissions) 104 | is judge0.JUDGE0_IMPLICIT_EXTRA_CE_CLIENT 105 | ) 106 | -------------------------------------------------------------------------------- /src/judge0/retry.py: -------------------------------------------------------------------------------- 1 | import time 2 | from abc import ABC, abstractmethod 3 | 4 | 5 | class RetryStrategy(ABC): 6 | """Abstract base class that defines the interface for any retry strategy. 7 | 8 | See :obj:`MaxRetries`, :obj:`MaxWaitTime`, and :obj:`RegularPeriodRetry` for 9 | example implementations. 10 | """ 11 | 12 | @abstractmethod 13 | def is_done(self) -> bool: 14 | """Check if the retry strategy has exhausted its retries.""" 15 | pass 16 | 17 | @abstractmethod 18 | def wait(self) -> None: 19 | """Delay implementation before the next retry attempt.""" 20 | pass 21 | 22 | @abstractmethod 23 | def step(self) -> None: 24 | """Update internal attributes of the retry strategy.""" 25 | pass 26 | 27 | 28 | class MaxRetries(RetryStrategy): 29 | """Check for submissions status every 100 ms and retry a maximum of 30 | `max_retries` times. 31 | 32 | Parameters 33 | ---------- 34 | max_retries : int 35 | Max number of retries. 36 | """ 37 | 38 | def __init__(self, max_retries: int = 20): 39 | if max_retries < 1: 40 | raise ValueError("max_retries must be at least 1.") 41 | self.n_retries = 0 42 | self.max_retries = max_retries 43 | 44 | def step(self): 45 | """Increment the number of retries by one.""" 46 | self.n_retries += 1 47 | 48 | def wait(self): 49 | """Wait for 0.1 seconds between retries.""" 50 | time.sleep(0.1) 51 | 52 | def is_done(self) -> bool: 53 | """Check if the number of retries is bigger or equal to specified 54 | maximum number of retries.""" 55 | return self.n_retries >= self.max_retries 56 | 57 | 58 | class MaxWaitTime(RetryStrategy): 59 | """Check for submissions status every 100 ms and wait for all submissions 60 | a maximum of `max_wait_time` (seconds). 61 | 62 | Parameters 63 | ---------- 64 | max_wait_time_sec : float 65 | Maximum waiting time (in seconds). 66 | """ 67 | 68 | def __init__(self, max_wait_time_sec: float = 5 * 60): 69 | self.max_wait_time_sec = max_wait_time_sec 70 | self.total_wait_time = 0 71 | 72 | def step(self): 73 | """Add 0.1 seconds to total waiting time.""" 74 | self.total_wait_time += 0.1 75 | 76 | def wait(self): 77 | """Wait (sleep) for 0.1 seconds.""" 78 | time.sleep(0.1) 79 | 80 | def is_done(self): 81 | """Check if the total waiting time is bigger or equal to the specified 82 | maximum waiting time.""" 83 | return self.total_wait_time >= self.max_wait_time_sec 84 | 85 | 86 | class RegularPeriodRetry(RetryStrategy): 87 | """Check for submissions status periodically for indefinite amount of time. 88 | 89 | Parameters 90 | ---------- 91 | wait_time_sec : float 92 | Wait time between retries (in seconds). 93 | """ 94 | 95 | def __init__(self, wait_time_sec: float = 0.1): 96 | self.wait_time_sec = wait_time_sec 97 | 98 | def wait(self): 99 | """Wait for `wait_time_sec` seconds.""" 100 | time.sleep(self.wait_time_sec) 101 | 102 | def is_done(self) -> bool: 103 | """Return False, as this retry strategy is indefinite.""" 104 | return False 105 | 106 | def step(self) -> None: 107 | """Satisfy the interface with a dummy implementation.""" 108 | pass 109 | -------------------------------------------------------------------------------- /docs/source/in_depth/client_resolution.rst: -------------------------------------------------------------------------------- 1 | Client Resolution 2 | ================= 3 | 4 | The Judge0 Python SDK supports two main client flavors: Community Edition (CE) and Extra Community Edition (Extra CE), which correspond to the Judge0 Cloud editions. When you use functions like ``judge0.run`` or ``judge0.execute`` without explicitly providing a client instance, the SDK automatically resolves and initializes a client for you. This process, handled by the internal ``_get_implicit_client`` function, follows a specific order of priority. 5 | 6 | The SDK determines which client to use based on environment variables. Here's the resolution order: 7 | 8 | 1. **Custom Client (Self-Hosted)** 9 | 10 | The SDK first checks for a self-hosted Judge0 instance. If you have your own deployment of Judge0, you can configure the SDK to use it by setting the following environment variables: 11 | 12 | * For CE flavor: 13 | * ``JUDGE0_CE_ENDPOINT``: The URL of your self-hosted Judge0 CE instance. 14 | * ``JUDGE0_CE_AUTH_HEADERS``: A JSON string representing the authentication headers. 15 | * For Extra CE flavor: 16 | * ``JUDGE0_EXTRA_CE_ENDPOINT``: The URL of your self-hosted Judge0 Extra CE instance. 17 | * ``JUDGE0_EXTRA_CE_AUTH_HEADERS``: A JSON string representing the authentication headers. 18 | 19 | If these variables are set, the SDK will initialize a ``Client`` instance with your custom endpoint and headers. 20 | 21 | 2. **Hub Clients** 22 | 23 | If a custom client is not configured, the SDK will try to find API keys for one of the supported hub clients. The SDK checks for the following environment variables in order: 24 | 25 | * **Judge0 Cloud**: 26 | * ``JUDGE0_CLOUD_CE_AUTH_HEADERS`` for ``Judge0CloudCE`` 27 | * ``JUDGE0_CLOUD_EXTRA_CE_AUTH_HEADERS` for ``Judge0CloudExtraCE`` 28 | 29 | * **RapidAPI**: 30 | * ``JUDGE0_RAPID_API_KEY`` for both ``RapidJudge0CE`` and ``RapidJudge0ExtraCE`` 31 | 32 | * **AllThingsDev**: 33 | * ``JUDGE0_ATD_API_KEY`` for both ``ATDJudge0CE`` and ``ATDJudge0ExtraCE`` 34 | 35 | The first API key found determines the client that will be used. 36 | 37 | 3. **Preview Client** 38 | 39 | If none of the above environment variables are set, the SDK falls back to using a **preview client**. This is an unauthenticated client that connects to the official Judge0 Cloud service. It initializes ``Judge0CloudCE()`` and ``Judge0CloudExtraCE()`` for the CE and Extra CE flavors, respectively. 40 | 41 | When the preview client is used, a warning message is logged to the console, as this option is not recommended for production use. To suppress this warning, you can set the ``JUDGE0_SUPPRESS_PREVIEW_WARNING`` environment variable. 42 | 43 | Example Resolution Flow 44 | ----------------------- 45 | 46 | When you call a function like ``judge0.run(..., flavor=judge0.CE)``, the SDK will: 47 | 48 | 1. Check if ``JUDGE0_IMPLICIT_CE_CLIENT`` is already initialized. If so, use it. 49 | 2. Check for ``JUDGE0_CE_ENDPOINT`` and ``JUDGE0_CE_AUTH_HEADERS`` to configure a ``Client``. 50 | 3. Check for ``JUDGE0_CLOUD_CE_AUTH_HEADERS`` to configure a ``Judge0CloudCE`` client. 51 | 4. Check for ``JUDGE0_RAPID_API_KEY`` to configure a ``RapidJudge0CE`` client. 52 | 5. Check for ``JUDGE0_ATD_API_KEY`` to configure an ``ATDJudge0CE`` client. 53 | 6. If none of the above are found, initialize a preview ``Judge0CloudCE`` client and log a warning. 54 | 55 | This implicit client resolution makes it easy to get started with the Judge0 Python SDK while providing the flexibility to configure it for different environments and services. 56 | -------------------------------------------------------------------------------- /docs/source/contributors_guide/contributing.rst: -------------------------------------------------------------------------------- 1 | Contributing 2 | ============ 3 | 4 | Preparing the development setup 5 | ------------------------------- 6 | 7 | 1. Install `uv `_. 8 | 2. Fork the repository (in GitHub) and clone the forked repository: 9 | 10 | .. code-block:: console 11 | 12 | $ git clone https://github.com//judge0-python 13 | $ cd judge0-python 14 | 15 | 3. Prepare the virtual environment and dependencies: 16 | 17 | .. code-block:: console 18 | 19 | $ uv sync --group dev 20 | 21 | 4. Install the necessary development tools: 22 | 23 | .. code-block:: console 24 | 25 | $ uv tool install pre-commit 26 | 27 | 5. Make sure all the necessary tools are installed: 28 | 29 | .. code-block:: console 30 | 31 | $ uv tool list 32 | 33 | 6. Finally, install the pre-commit hooks: 34 | 35 | .. code-block:: console 36 | 37 | $ uv tool run pre-commit install 38 | 39 | Building documentation 40 | ---------------------- 41 | 42 | Documentation is built using Sphinx. While working on a documentation, you can run 43 | 44 | .. code-block:: console 45 | 46 | $ cd docs 47 | $ make livehtml 48 | 49 | The ``make livehtml`` command will start a live HTTP server, watch the `docs` directory for changes, 50 | and rebuild the documentation on every change, enabling you to continuously work on the documentation, 51 | without having to manually rebuild it after every change. 52 | 53 | You can inspect the changes by opening the ``http://localhost:8000`` in your browser. 54 | 55 | .. You'll see a different output since the documentation is build with 56 | .. `sphinx-multiversion `_ extension. 57 | 58 | Testing 59 | ------- 60 | 61 | .. warning:: 62 | If you are implementing features or fixing bugs, you are expected to have 63 | all API keys (RapidAPI and ATD) setup and set in you 64 | environment variables - ``JUDGE0_RAPID_API_KEY``, 65 | and ``JUDGE0_ATD_API_KEY``. 66 | 67 | Every bug fix or new feature should have tests for it. The tests are located in 68 | the ``tests`` directory and are written using `pytest `_. 69 | 70 | While working with the tests, you should use the following fixtures: 71 | 72 | * ``ce_client`` - a client, chosen based on the environment variables set, that uses the CE flavor of the client. 73 | * ``extra_ce_client`` - a client, chosen based on the environment variables set, that uses the Extra CE flavor of the client. 74 | 75 | The ``ce_client`` and ``extra_ce_client`` are fixtures that 76 | return a client based on the environment variables set. This enables you to 77 | run the full test suite locally, but also to run the tests on the CI pipeline 78 | without changing the tests. 79 | 80 | You can use the fixtures in your tests like this: 81 | 82 | .. code-block:: python 83 | 84 | def test_my_test(request): 85 | client = request.getfixturevalue("ce_client") # or extra_ce_client 86 | 87 | To run the tests locally, you can use the following command: 88 | 89 | .. code-block:: console 90 | 91 | $ pytest tests -k '' 92 | 93 | This will enable you to run a single test, without incurring the cost of 94 | running the full test suite. If you want to run the full test suite, you can 95 | use the following command: 96 | 97 | .. code-block:: console 98 | 99 | $ pytest tests 100 | 101 | or you can create a draft PR and let the CI pipeline run the tests for you. 102 | The CI pipeline will run the tests on every PR, using a private instance 103 | of Judge0, so you can be sure that your changes are not breaking the existing 104 | functionality. 105 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | share/python-wheels/ 24 | *.egg-info/ 25 | .installed.cfg 26 | *.egg 27 | MANIFEST 28 | 29 | # PyInstaller 30 | # Usually these files are written by a python script from a template 31 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 32 | *.manifest 33 | *.spec 34 | 35 | # Installer logs 36 | pip-log.txt 37 | pip-delete-this-directory.txt 38 | 39 | # Unit test / coverage reports 40 | htmlcov/ 41 | .tox/ 42 | .nox/ 43 | .coverage 44 | .coverage.* 45 | .cache 46 | nosetests.xml 47 | coverage.xml 48 | *.cover 49 | *.py,cover 50 | .hypothesis/ 51 | .pytest_cache/ 52 | cover/ 53 | 54 | # Translations 55 | *.mo 56 | *.pot 57 | 58 | # Django stuff: 59 | *.log 60 | local_settings.py 61 | db.sqlite3 62 | db.sqlite3-journal 63 | 64 | # Flask stuff: 65 | instance/ 66 | .webassets-cache 67 | 68 | # Scrapy stuff: 69 | .scrapy 70 | 71 | # Sphinx documentation 72 | docs/_build/ 73 | 74 | # PyBuilder 75 | .pybuilder/ 76 | target/ 77 | 78 | # Jupyter Notebook 79 | .ipynb_checkpoints 80 | 81 | # IPython 82 | profile_default/ 83 | ipython_config.py 84 | 85 | # pyenv 86 | # For a library or package, you might want to ignore these files since the code is 87 | # intended to run in multiple environments; otherwise, check them in: 88 | # .python-version 89 | 90 | # pipenv 91 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 92 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 93 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 94 | # install all needed dependencies. 95 | #Pipfile.lock 96 | 97 | # poetry 98 | # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. 99 | # This is especially recommended for binary packages to ensure reproducibility, and is more 100 | # commonly ignored for libraries. 101 | # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control 102 | #poetry.lock 103 | 104 | # pdm 105 | # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. 106 | #pdm.lock 107 | # pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it 108 | # in version control. 109 | # https://pdm.fming.dev/latest/usage/project/#working-with-version-control 110 | .pdm.toml 111 | .pdm-python 112 | .pdm-build/ 113 | 114 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm 115 | __pypackages__/ 116 | 117 | # Celery stuff 118 | celerybeat-schedule 119 | celerybeat.pid 120 | 121 | # SageMath parsed files 122 | *.sage.py 123 | 124 | # Environments 125 | .env 126 | .venv 127 | env/ 128 | venv/ 129 | ENV/ 130 | env.bak/ 131 | venv.bak/ 132 | 133 | # Spyder project settings 134 | .spyderproject 135 | .spyproject 136 | 137 | # Rope project settings 138 | .ropeproject 139 | 140 | # mkdocs documentation 141 | /site 142 | 143 | # mypy 144 | .mypy_cache/ 145 | .dmypy.json 146 | dmypy.json 147 | 148 | # Pyre type checker 149 | .pyre/ 150 | 151 | # pytype static type analyzer 152 | .pytype/ 153 | 154 | # Cython debug symbols 155 | cython_debug/ 156 | 157 | # PyCharm 158 | # JetBrains specific template is maintained in a separate JetBrains.gitignore that can 159 | # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore 160 | # and can be added to the global gitignore or merged into this file. For a more nuclear 161 | # option (not recommended) you can uncomment the following to ignore the entire idea folder. 162 | #.idea/ 163 | -------------------------------------------------------------------------------- /src/judge0/filesystem.py: -------------------------------------------------------------------------------- 1 | import copy 2 | import io 3 | import zipfile 4 | 5 | from base64 import b64decode, b64encode 6 | from typing import Optional, Union 7 | 8 | from pydantic import BaseModel 9 | 10 | from .base_types import Iterable 11 | 12 | 13 | class File(BaseModel): 14 | """File object for storing file content. 15 | 16 | Parameters 17 | ---------- 18 | name : str 19 | File name. 20 | content : str or bytes, optional 21 | File content. If str is provided, it will be encoded to bytes. 22 | """ 23 | 24 | name: str 25 | content: Optional[Union[str, bytes]] = None 26 | 27 | def __init__(self, **data): 28 | super().__init__(**data) 29 | # Let's keep content attribute internally encoded as bytes. 30 | if isinstance(self.content, str): 31 | self.content = self.content.encode() 32 | elif isinstance(self.content, bytes): 33 | self.content = self.content 34 | else: 35 | self.content = b"" 36 | 37 | def __str__(self): 38 | return self.content.decode(errors="backslashreplace") 39 | 40 | 41 | class Filesystem(BaseModel): 42 | """Filesystem object for storing multiple files. 43 | 44 | Parameters 45 | ---------- 46 | content : str or bytes or File or Iterable[File] or Filesystem, optional 47 | Filesystem content. If str or bytes is provided, it will be decoded to 48 | files. 49 | """ 50 | 51 | files: list[File] = [] 52 | 53 | def __init__(self, **data): 54 | content = data.pop("content", None) 55 | super().__init__(**data) 56 | self.files = [] 57 | 58 | if isinstance(content, (bytes, str)): 59 | if isinstance(content, bytes): 60 | zip_bytes = content 61 | else: 62 | zip_bytes = b64decode(content.encode()) 63 | 64 | with zipfile.ZipFile(io.BytesIO(zip_bytes), "r") as zip_file: 65 | for file_name in zip_file.namelist(): 66 | with zip_file.open(file_name) as fp: 67 | self.files.append(File(name=file_name, content=fp.read())) 68 | elif isinstance(content, Iterable): 69 | self.files = list(content) 70 | elif isinstance(content, File): 71 | self.files = [content] 72 | elif isinstance(content, Filesystem): 73 | self.files = copy.deepcopy(content.files) 74 | elif content is None: 75 | self.files = [] 76 | else: 77 | raise ValueError( 78 | "Unsupported type for content argument. Expected " 79 | "one of str, bytes, File, Iterable[File], or Filesystem, " 80 | f"got {type(content)}." 81 | ) 82 | 83 | def __repr__(self) -> str: 84 | content_encoded = b64encode(self.encode()).decode() 85 | return f"{self.__class__.__name__}(content={content_encoded!r})" 86 | 87 | def encode(self) -> bytes: 88 | """Encode Filesystem object to bytes.""" 89 | zip_buffer = io.BytesIO() 90 | with zipfile.ZipFile(zip_buffer, "w") as zip_file: 91 | for file in self.files: 92 | zip_file.writestr(file.name, file.content) 93 | return zip_buffer.getvalue() 94 | 95 | def find(self, name: str) -> Optional[File]: 96 | """Find file by name in Filesystem object. 97 | 98 | Parameters 99 | ---------- 100 | name : str 101 | File name to find. 102 | 103 | Returns 104 | ------- 105 | File or None 106 | Found File object or None if not found. 107 | """ 108 | if name.startswith("./"): 109 | name = name[2:] 110 | elif name.startswith("/"): 111 | name = name[1:] 112 | 113 | matches = [f for f in self.files if f.name == name] 114 | return matches[0] if matches else None 115 | 116 | def __str__(self) -> str: 117 | """Create string representation of Filesystem object.""" 118 | return b64encode(self.encode()).decode() 119 | 120 | def __iter__(self): 121 | return iter(self.files) 122 | -------------------------------------------------------------------------------- /tests/conftest.py: -------------------------------------------------------------------------------- 1 | import json 2 | import os 3 | 4 | import pytest 5 | from dotenv import load_dotenv 6 | 7 | from judge0 import clients, RegularPeriodRetry 8 | 9 | load_dotenv() 10 | 11 | 12 | @pytest.fixture(scope="session") 13 | def custom_ce_client(): 14 | endpoint = os.getenv("JUDGE0_CE_ENDPOINT") 15 | auth_headers = os.getenv("JUDGE0_CE_AUTH_HEADERS") 16 | 17 | if endpoint is None or auth_headers is None: 18 | return None 19 | else: 20 | return clients.Client(endpoint=endpoint, auth_headers=json.loads(auth_headers)) 21 | 22 | 23 | @pytest.fixture(scope="session") 24 | def custom_extra_ce_client(): 25 | endpoint = os.getenv("JUDGE0_EXTRA_CE_ENDPOINT") 26 | auth_headers = os.getenv("JUDGE0_EXTRA_CE_AUTH_HEADERS") 27 | 28 | if endpoint is None or auth_headers is None: 29 | return None 30 | else: 31 | return clients.Client(endpoint=endpoint, auth_headers=json.loads(auth_headers)) 32 | 33 | 34 | @pytest.fixture(scope="session") 35 | def atd_ce_client(): 36 | api_key = os.getenv("JUDGE0_ATD_API_KEY") 37 | 38 | if api_key is None: 39 | return None 40 | else: 41 | return clients.ATDJudge0CE(api_key) 42 | 43 | 44 | @pytest.fixture(scope="session") 45 | def atd_extra_ce_client(): 46 | api_key = os.getenv("JUDGE0_ATD_API_KEY") 47 | 48 | if api_key is None: 49 | return None 50 | else: 51 | return clients.ATDJudge0ExtraCE(api_key) 52 | 53 | 54 | @pytest.fixture(scope="session") 55 | def rapid_ce_client(): 56 | api_key = os.getenv("JUDGE0_RAPID_API_KEY") 57 | 58 | if api_key is None: 59 | return None 60 | else: 61 | return clients.RapidJudge0CE(api_key) 62 | 63 | 64 | @pytest.fixture(scope="session") 65 | def rapid_extra_ce_client(): 66 | api_key = os.getenv("JUDGE0_RAPID_API_KEY") 67 | 68 | if api_key is None: 69 | return None 70 | else: 71 | return clients.RapidJudge0ExtraCE(api_key) 72 | 73 | 74 | @pytest.fixture(scope="session") 75 | def judge0_cloud_ce_client(): 76 | auth_headers = os.getenv("JUDGE0_CLOUD_CE_AUTH_HEADERS") 77 | 78 | if auth_headers is None: 79 | return None 80 | else: 81 | return clients.Judge0CloudCE(auth_headers) 82 | 83 | 84 | @pytest.fixture(scope="session") 85 | def judge0_cloud_extra_ce_client(): 86 | auth_headers = os.getenv("JUDGE0_CLOUD_EXTRA_CE_AUTH_HEADERS") 87 | 88 | if auth_headers is None: 89 | return None 90 | else: 91 | return clients.Judge0CloudExtraCE(auth_headers) 92 | 93 | 94 | @pytest.fixture(scope="session") 95 | def preview_ce_client() -> clients.Judge0CloudCE: 96 | return clients.Judge0CloudCE(retry_strategy=RegularPeriodRetry(0.5)) 97 | 98 | 99 | @pytest.fixture(scope="session") 100 | def preview_extra_ce_client() -> clients.Judge0CloudExtraCE: 101 | return clients.Judge0CloudExtraCE(retry_strategy=RegularPeriodRetry(0.5)) 102 | 103 | 104 | @pytest.fixture(scope="session") 105 | def ce_client( 106 | custom_ce_client, 107 | judge0_cloud_ce_client, 108 | rapid_ce_client, 109 | atd_ce_client, 110 | preview_ce_client, 111 | ): 112 | if custom_ce_client is not None: 113 | return custom_ce_client 114 | if judge0_cloud_ce_client is not None: 115 | return judge0_cloud_ce_client 116 | if rapid_ce_client is not None: 117 | return rapid_ce_client 118 | if atd_ce_client is not None: 119 | return atd_ce_client 120 | if preview_ce_client is not None: 121 | return preview_ce_client 122 | 123 | pytest.fail("No CE client available for testing. This error should not happen!") 124 | 125 | 126 | @pytest.fixture(scope="session") 127 | def extra_ce_client( 128 | custom_extra_ce_client, 129 | judge0_cloud_extra_ce_client, 130 | rapid_extra_ce_client, 131 | atd_extra_ce_client, 132 | preview_extra_ce_client, 133 | ): 134 | if custom_extra_ce_client is not None: 135 | return custom_extra_ce_client 136 | if judge0_cloud_extra_ce_client is not None: 137 | return judge0_cloud_extra_ce_client 138 | if rapid_extra_ce_client is not None: 139 | return rapid_extra_ce_client 140 | if atd_extra_ce_client is not None: 141 | return atd_extra_ce_client 142 | if preview_extra_ce_client is not None: 143 | return preview_extra_ce_client 144 | 145 | pytest.fail( 146 | "No Extra CE client available for testing. This error should not happen!" 147 | ) 148 | -------------------------------------------------------------------------------- /docs/source/conf.py: -------------------------------------------------------------------------------- 1 | # Configuration file for the Sphinx documentation builder. 2 | # 3 | # For the full list of built-in configuration values, see the documentation: 4 | # https://www.sphinx-doc.org/en/master/usage/configuration.html 5 | 6 | # -- Project information ----------------------------------------------------- 7 | # https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information 8 | 9 | 10 | import os 11 | import sys 12 | 13 | from sphinxawesome_theme.postprocess import Icons 14 | 15 | project = "Judge0 Python SDK" 16 | copyright = "2025, Judge0" 17 | author = "Judge0" 18 | release = "" 19 | 20 | # -- General configuration --------------------------------------------------- 21 | # https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration 22 | 23 | extensions = [ 24 | "sphinx.ext.autodoc", 25 | "sphinx.ext.napoleon", 26 | # "sphinx.ext.autosummary", 27 | "sphinx_autodoc_typehints", 28 | "sphinx_multiversion", 29 | ] 30 | 31 | templates_path = ["_templates"] 32 | exclude_patterns = [] 33 | 34 | # -- Options for HTML output ------------------------------------------------- 35 | # https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output 36 | 37 | html_title = project 38 | html_theme = "sphinxawesome_theme" 39 | html_theme_options = { 40 | "show_scrolltop": True, 41 | "extra_header_link_icons": { 42 | "repository on GitHub": { 43 | "link": "https://github.com/judge0/judge0-python", 44 | "icon": ( 45 | '' 48 | '' 67 | ), 68 | }, 69 | }, 70 | "awesome_external_links": True, 71 | "main_nav_links": { 72 | "Home": "index", 73 | "Judge0": "https://judge0.com/", 74 | }, 75 | } 76 | html_show_sphinx = False 77 | html_sidebars = { 78 | "**": [ 79 | "sidebar_main_nav_links.html", 80 | "sidebar_toc.html", 81 | "versioning.html", 82 | ], 83 | } 84 | html_logo = "../assets/logo.png" 85 | html_favicon = html_logo 86 | pygments_style = "sphinx" 87 | 88 | sys.path.insert(0, os.path.abspath("../../src/")) # Adjust as needed 89 | 90 | # -- Awesome theme config -- 91 | html_permalinks_icon = Icons.permalinks_icon 92 | 93 | autodoc_default_options = { 94 | "members": True, 95 | "undoc-members": False, 96 | "private-members": False, 97 | "special-members": False, 98 | "inherited-members": False, 99 | } 100 | autodoc_mock_imports = ["requests", "pydantic"] 101 | 102 | napoleon_google_docstring = False 103 | 104 | # Whitelist pattern for tags (set to None to ignore all tags) 105 | smv_tag_whitelist = r"^v[0-9]+\.[0-9]+\.[0-9]+$" 106 | # Whitelist pattern for branches (set to None to ignore all branches) 107 | smv_branch_whitelist = r"^master$" 108 | # Whitelist pattern for remotes (set to None to use local branches only) 109 | smv_remote_whitelist = None 110 | # Pattern for released versions 111 | smv_released_pattern = "" # r"^tags/.*$" 112 | # Format for versioned output directories inside the build directory 113 | smv_outputdir_format = "{ref.name}" 114 | # Determines whether remote or local git branches/tags are preferred if their 115 | # output dirs conflict 116 | smv_prefer_remote_refs = False 117 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | We as members, contributors, and leaders pledge to make participation in our 6 | community a harassment-free experience for everyone, regardless of age, body 7 | size, visible or invisible disability, ethnicity, sex characteristics, gender 8 | identity and expression, level of experience, education, socio-economic status, 9 | nationality, personal appearance, race, religion, or sexual identity 10 | and orientation. 11 | 12 | We pledge to act and interact in ways that contribute to an open, welcoming, 13 | diverse, inclusive, and healthy community. 14 | 15 | ## Our Standards 16 | 17 | Examples of behavior that contributes to a positive environment for our 18 | community include: 19 | 20 | * Demonstrating empathy and kindness toward other people 21 | * Being respectful of differing opinions, viewpoints, and experiences 22 | * Giving and gracefully accepting constructive feedback 23 | * Accepting responsibility and apologizing to those affected by our mistakes, 24 | and learning from the experience 25 | * Focusing on what is best not just for us as individuals, but for the 26 | overall community 27 | 28 | Examples of unacceptable behavior include: 29 | 30 | * The use of sexualized language or imagery, and sexual attention or 31 | advances of any kind 32 | * Trolling, insulting or derogatory comments, and personal or political attacks 33 | * Public or private harassment 34 | * Publishing others' private information, such as a physical or email 35 | address, without their explicit permission 36 | * Other conduct which could reasonably be considered inappropriate in a 37 | professional setting 38 | 39 | ## Enforcement Responsibilities 40 | 41 | Community leaders are responsible for clarifying and enforcing our standards of 42 | acceptable behavior and will take appropriate and fair corrective action in 43 | response to any behavior that they deem inappropriate, threatening, offensive, 44 | or harmful. 45 | 46 | Community leaders have the right and responsibility to remove, edit, or reject 47 | comments, commits, code, wiki edits, issues, and other contributions that are 48 | not aligned to this Code of Conduct, and will communicate reasons for moderation 49 | decisions when appropriate. 50 | 51 | ## Scope 52 | 53 | This Code of Conduct applies within all community spaces, and also applies when 54 | an individual is officially representing the community in public spaces. 55 | Examples of representing our community include using an official e-mail address, 56 | posting via an official social media account, or acting as an appointed 57 | representative at an online or offline event. 58 | 59 | ## Enforcement 60 | 61 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 62 | reported to the community leaders responsible for enforcement at 63 | contact@judge0.com. 64 | All complaints will be reviewed and investigated promptly and fairly. 65 | 66 | All community leaders are obligated to respect the privacy and security of the 67 | reporter of any incident. 68 | 69 | ## Enforcement Guidelines 70 | 71 | Community leaders will follow these Community Impact Guidelines in determining 72 | the consequences for any action they deem in violation of this Code of Conduct: 73 | 74 | ### 1. Correction 75 | 76 | **Community Impact**: Use of inappropriate language or other behavior deemed 77 | unprofessional or unwelcome in the community. 78 | 79 | **Consequence**: A private, written warning from community leaders, providing 80 | clarity around the nature of the violation and an explanation of why the 81 | behavior was inappropriate. A public apology may be requested. 82 | 83 | ### 2. Warning 84 | 85 | **Community Impact**: A violation through a single incident or series 86 | of actions. 87 | 88 | **Consequence**: A warning with consequences for continued behavior. No 89 | interaction with the people involved, including unsolicited interaction with 90 | those enforcing the Code of Conduct, for a specified period of time. This 91 | includes avoiding interactions in community spaces as well as external channels 92 | like social media. Violating these terms may lead to a temporary or 93 | permanent ban. 94 | 95 | ### 3. Temporary Ban 96 | 97 | **Community Impact**: A serious violation of community standards, including 98 | sustained inappropriate behavior. 99 | 100 | **Consequence**: A temporary ban from any sort of interaction or public 101 | communication with the community for a specified period of time. No public or 102 | private interaction with the people involved, including unsolicited interaction 103 | with those enforcing the Code of Conduct, is allowed during this period. 104 | Violating these terms may lead to a permanent ban. 105 | 106 | ### 4. Permanent Ban 107 | 108 | **Community Impact**: Demonstrating a pattern of violation of community 109 | standards, including sustained inappropriate behavior, harassment of an 110 | individual, or aggression toward or disparagement of classes of individuals. 111 | 112 | **Consequence**: A permanent ban from any sort of public interaction within 113 | the community. 114 | 115 | ## Attribution 116 | 117 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], 118 | version 2.0, available at 119 | https://www.contributor-covenant.org/version/2/0/code_of_conduct.html. 120 | 121 | Community Impact Guidelines were inspired by [Mozilla's code of conduct 122 | enforcement ladder](https://github.com/mozilla/diversity). 123 | 124 | [homepage]: https://www.contributor-covenant.org 125 | 126 | For answers to common questions about this code of conduct, see the FAQ at 127 | https://www.contributor-covenant.org/faq. Translations are available at 128 | https://www.contributor-covenant.org/translations. 129 | -------------------------------------------------------------------------------- /examples/0006_exe.py: -------------------------------------------------------------------------------- 1 | from base64 import b64decode 2 | 3 | import judge0 4 | 5 | source_code = b64decode( 6 | "f0VMRgIBAQAAAAAAAAAAAAIAPgABAAAAAABAAAAAAABAAAAAAAAAAEAQAAAAAAAAAAAAAEAAOAABAEAABAADAAEAAAAFAAAAABAAAAAAAAAAAEAAAAAAAAAAQAAAAAAAJQAAAAAAAAAlAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADHAjVANsAG+GABAAInHDwUx/41HPA8FAGhlbGxvLCB3b3JsZAoALnNoc3RydGFiAC50ZXh0AC5yb2RhdGEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACwAAAAEAAAAGAAAAAAAAAAAAQAAAAAAAABAAAAAAAAAXAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAABEAAAABAAAAAgAAAAAAAAAYAEAAAAAAABgQAAAAAAAADQAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAABAAAAAwAAAAAAAAAAAAAAAAAAAAAAAAAlEAAAAAAAABkAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAA" # noqa: E501 7 | ) 8 | result = judge0.run(source_code=source_code, language=judge0.EXECUTABLE) 9 | print(result.stdout) 10 | -------------------------------------------------------------------------------- /src/judge0/data.py: -------------------------------------------------------------------------------- 1 | from .base_types import LanguageAlias 2 | 3 | LANGUAGE_TO_LANGUAGE_ID = { 4 | "1.13.1": { 5 | LanguageAlias.ASSEMBLY: 45, 6 | LanguageAlias.BASH: 46, 7 | LanguageAlias.BASIC: 47, 8 | LanguageAlias.C: 50, 9 | LanguageAlias.CLOJURE: 86, 10 | LanguageAlias.COBOL: 77, 11 | LanguageAlias.COMMON_LISP: 55, 12 | LanguageAlias.CPP: 52, 13 | LanguageAlias.CPP_CLANG: 76, 14 | LanguageAlias.CPP_GCC: 52, 15 | LanguageAlias.CSHARP: 51, 16 | LanguageAlias.CSHARP_MONO: 51, 17 | LanguageAlias.C_CLANG: 75, 18 | LanguageAlias.C_GCC: 50, 19 | LanguageAlias.D: 56, 20 | LanguageAlias.ELIXIR: 57, 21 | LanguageAlias.ERLANG: 58, 22 | LanguageAlias.EXECUTABLE: 44, 23 | LanguageAlias.FORTRAN: 59, 24 | LanguageAlias.FSHARP: 87, 25 | LanguageAlias.GO: 60, 26 | LanguageAlias.GROOVY: 88, 27 | LanguageAlias.HASKELL: 61, 28 | LanguageAlias.JAVA: 62, 29 | LanguageAlias.JAVASCRIPT: 63, 30 | LanguageAlias.JAVA_OPENJDK: 62, 31 | LanguageAlias.KOTLIN: 78, 32 | LanguageAlias.LUA: 64, 33 | LanguageAlias.MULTI_FILE: 89, 34 | LanguageAlias.OBJECTIVE_C: 79, 35 | LanguageAlias.OCAML: 65, 36 | LanguageAlias.OCTAVE: 66, 37 | LanguageAlias.PASCAL: 67, 38 | LanguageAlias.PERL: 85, 39 | LanguageAlias.PHP: 68, 40 | LanguageAlias.PLAIN_TEXT: 43, 41 | LanguageAlias.PROLOG: 69, 42 | LanguageAlias.PYTHON: 71, 43 | LanguageAlias.PYTHON2: 70, 44 | LanguageAlias.PYTHON3: 71, 45 | LanguageAlias.R: 80, 46 | LanguageAlias.RUBY: 72, 47 | LanguageAlias.RUST: 73, 48 | LanguageAlias.SCALA: 81, 49 | LanguageAlias.SQLITE: 82, 50 | LanguageAlias.SWIFT: 83, 51 | LanguageAlias.TYPESCRIPT: 74, 52 | LanguageAlias.VISUAL_BASIC: 84, 53 | }, 54 | "1.13.1-extra": { 55 | LanguageAlias.BOSQUE: 11, 56 | LanguageAlias.C: 1, 57 | LanguageAlias.C3: 3, 58 | LanguageAlias.CPP: 2, 59 | LanguageAlias.CPP_CLANG: 2, 60 | LanguageAlias.CPP_TEST: 12, 61 | LanguageAlias.CPP_TEST_CLANG: 15, 62 | LanguageAlias.CPP_TEST_GCC: 12, 63 | LanguageAlias.CSHARP: 22, 64 | LanguageAlias.CSHARP_MONO: 22, 65 | LanguageAlias.CSHARP_DOTNET: 21, 66 | LanguageAlias.CSHARP_TEST: 23, 67 | LanguageAlias.C_CLANG: 1, 68 | LanguageAlias.FSHARP: 24, 69 | LanguageAlias.JAVA: 4, 70 | LanguageAlias.JAVA_OPENJDK: 4, 71 | LanguageAlias.JAVA_TEST: 5, 72 | LanguageAlias.MPI_C: 6, 73 | LanguageAlias.MPI_CPP: 7, 74 | LanguageAlias.MPI_PYTHON: 8, 75 | LanguageAlias.MULTI_FILE: 89, 76 | LanguageAlias.NIM: 9, 77 | LanguageAlias.PYTHON: 10, 78 | LanguageAlias.PYTHON3: 10, 79 | LanguageAlias.PYTHON_FOR_ML: 10, 80 | LanguageAlias.VISUAL_BASIC: 20, 81 | }, 82 | "1.14.0": { 83 | LanguageAlias.ASSEMBLY: 45, 84 | LanguageAlias.BASH: 46, 85 | LanguageAlias.BASIC: 47, 86 | LanguageAlias.C: 103, 87 | LanguageAlias.CLOJURE: 86, 88 | LanguageAlias.COBOL: 77, 89 | LanguageAlias.COMMON_LISP: 55, 90 | LanguageAlias.CPP: 105, 91 | LanguageAlias.CPP_CLANG: 76, 92 | LanguageAlias.CPP_GCC: 105, 93 | LanguageAlias.CSHARP: 51, 94 | LanguageAlias.CSHARP_MONO: 51, 95 | LanguageAlias.C_CLANG: 110, 96 | LanguageAlias.C_GCC: 103, 97 | LanguageAlias.D: 56, 98 | LanguageAlias.DART: 90, 99 | LanguageAlias.ELIXIR: 57, 100 | LanguageAlias.ERLANG: 58, 101 | LanguageAlias.EXECUTABLE: 44, 102 | LanguageAlias.FORTRAN: 59, 103 | LanguageAlias.FSHARP: 87, 104 | LanguageAlias.GO: 107, 105 | LanguageAlias.GROOVY: 88, 106 | LanguageAlias.HASKELL: 61, 107 | LanguageAlias.JAVA: 62, 108 | LanguageAlias.JAVAFX: 96, 109 | LanguageAlias.JAVASCRIPT: 102, 110 | LanguageAlias.JAVA_JDK: 91, 111 | LanguageAlias.JAVA_OPENJDK: 62, 112 | LanguageAlias.KOTLIN: 111, 113 | LanguageAlias.LUA: 64, 114 | LanguageAlias.MULTI_FILE: 89, 115 | LanguageAlias.OBJECTIVE_C: 79, 116 | LanguageAlias.OCAML: 65, 117 | LanguageAlias.OCTAVE: 66, 118 | LanguageAlias.PASCAL: 67, 119 | LanguageAlias.PERL: 85, 120 | LanguageAlias.PHP: 98, 121 | LanguageAlias.PLAIN_TEXT: 43, 122 | LanguageAlias.PROLOG: 69, 123 | LanguageAlias.PYTHON: 113, 124 | LanguageAlias.PYTHON2: 70, 125 | LanguageAlias.PYTHON3: 113, 126 | LanguageAlias.R: 99, 127 | LanguageAlias.RUBY: 72, 128 | LanguageAlias.RUST: 108, 129 | LanguageAlias.SCALA: 112, 130 | LanguageAlias.SQLITE: 82, 131 | LanguageAlias.SWIFT: 83, 132 | LanguageAlias.TYPESCRIPT: 101, 133 | LanguageAlias.VISUAL_BASIC: 84, 134 | }, 135 | "1.14.0-extra": { 136 | LanguageAlias.BOSQUE: 11, 137 | LanguageAlias.C: 1, 138 | LanguageAlias.C3: 3, 139 | LanguageAlias.CPP: 2, 140 | LanguageAlias.CPP_CLANG: 2, 141 | LanguageAlias.CPP_TEST: 12, 142 | LanguageAlias.CPP_TEST_CLANG: 15, 143 | LanguageAlias.CPP_TEST_GCC: 12, 144 | LanguageAlias.CSHARP: 30, 145 | LanguageAlias.CSHARP_MONO: 22, 146 | LanguageAlias.CSHARP_DOTNET: 30, 147 | LanguageAlias.CSHARP_TEST: 23, 148 | LanguageAlias.C_CLANG: 1, 149 | LanguageAlias.FSHARP: 24, 150 | LanguageAlias.JAVA: 4, 151 | LanguageAlias.JAVA_OPENJDK: 4, 152 | LanguageAlias.JAVA_TEST: 5, 153 | LanguageAlias.MPI_C: 6, 154 | LanguageAlias.MPI_CPP: 7, 155 | LanguageAlias.MPI_PYTHON: 8, 156 | LanguageAlias.MULTI_FILE: 89, 157 | LanguageAlias.NIM: 9, 158 | LanguageAlias.PYTHON: 33, 159 | LanguageAlias.PYTHON2: 26, 160 | LanguageAlias.PYTHON2_PYPY: 26, 161 | LanguageAlias.PYTHON3: 33, 162 | LanguageAlias.PYTHON3_PYPY: 28, 163 | LanguageAlias.PYTHON_FOR_ML: 33, 164 | LanguageAlias.VISUAL_BASIC: 20, 165 | }, 166 | } 167 | -------------------------------------------------------------------------------- /src/judge0/base_types.py: -------------------------------------------------------------------------------- 1 | import copy 2 | 3 | from dataclasses import dataclass 4 | from enum import auto, IntEnum 5 | from typing import Optional, Protocol, runtime_checkable, Sequence, Union 6 | 7 | from pydantic import BaseModel 8 | 9 | Iterable = Sequence 10 | 11 | TestCaseType = Union["TestCase", list, tuple, dict] 12 | TestCases = Iterable[TestCaseType] 13 | 14 | 15 | @dataclass(frozen=True) 16 | class TestCase: 17 | """Test case data model.""" 18 | 19 | __test__ = False # Needed to avoid pytest warning 20 | 21 | input: Optional[str] = None 22 | expected_output: Optional[str] = None 23 | 24 | @classmethod 25 | def from_record( 26 | cls, test_case: Union[TestCaseType, None] 27 | ) -> Union["TestCase", None]: 28 | """Create a TestCase from built-in types. 29 | 30 | Parameters 31 | ---------- 32 | test_case: :obj:`TestCaseType` or None 33 | Test case data. 34 | 35 | Returns 36 | ------- 37 | TestCase or None 38 | Created TestCase object or None if test_case is None. 39 | """ 40 | if isinstance(test_case, (tuple, list)): 41 | test_case = { 42 | field: value 43 | for field, value in zip(("input", "expected_output"), test_case) 44 | } 45 | if isinstance(test_case, dict): 46 | return cls( 47 | input=test_case.get("input", None), 48 | expected_output=test_case.get("expected_output", None), 49 | ) 50 | if isinstance(test_case, cls): 51 | return copy.deepcopy(test_case) 52 | if test_case is None: 53 | return None 54 | raise ValueError( 55 | f"Cannot create TestCase object from object of type {type(test_case)}." 56 | ) 57 | 58 | 59 | @runtime_checkable 60 | class Encodable(Protocol): 61 | def encode(self) -> bytes: 62 | """Serialize the object to bytes.""" 63 | ... 64 | 65 | 66 | class Language(BaseModel): 67 | """Language data model. 68 | 69 | Stores information about a language supported by Judge0. 70 | """ 71 | 72 | id: int 73 | name: str 74 | is_archived: Optional[bool] = None 75 | source_file: Optional[str] = None 76 | compile_cmd: Optional[str] = None 77 | run_cmd: Optional[str] = None 78 | 79 | 80 | class LanguageAlias(IntEnum): 81 | """Language alias enumeration. 82 | 83 | Enumerates the programming languages supported by Judge0 client. Language 84 | alias is resolved to the latest version of the language supported by the 85 | selected client. 86 | """ 87 | 88 | ASSEMBLY = auto() 89 | BASH = auto() 90 | BASIC = auto() 91 | BOSQUE = auto() 92 | C = auto() 93 | C3 = auto() 94 | CLOJURE = auto() 95 | COBOL = auto() 96 | COMMON_LISP = auto() 97 | CPP = auto() 98 | CPP_CLANG = auto() 99 | CPP_GCC = auto() 100 | CPP_TEST = auto() 101 | CPP_TEST_CLANG = auto() 102 | CPP_TEST_GCC = auto() 103 | CSHARP = auto() 104 | CSHARP_DOTNET = auto() 105 | CSHARP_MONO = auto() 106 | CSHARP_TEST = auto() 107 | C_CLANG = auto() 108 | C_GCC = auto() 109 | D = auto() 110 | DART = auto() 111 | ELIXIR = auto() 112 | ERLANG = auto() 113 | EXECUTABLE = auto() 114 | FORTRAN = auto() 115 | FSHARP = auto() 116 | GO = auto() 117 | GROOVY = auto() 118 | HASKELL = auto() 119 | JAVA = auto() 120 | JAVAFX = auto() 121 | JAVASCRIPT = auto() 122 | JAVA_JDK = auto() 123 | JAVA_OPENJDK = auto() 124 | JAVA_TEST = auto() 125 | KOTLIN = auto() 126 | LUA = auto() 127 | MPI_C = auto() 128 | MPI_CPP = auto() 129 | MPI_PYTHON = auto() 130 | MULTI_FILE = auto() 131 | NIM = auto() 132 | OBJECTIVE_C = auto() 133 | OCAML = auto() 134 | OCTAVE = auto() 135 | PASCAL = auto() 136 | PERL = auto() 137 | PHP = auto() 138 | PLAIN_TEXT = auto() 139 | PROLOG = auto() 140 | PYTHON = auto() 141 | PYTHON2 = auto() 142 | PYTHON2_PYPY = auto() 143 | PYTHON3 = auto() 144 | PYTHON3_PYPY = auto() 145 | PYTHON_FOR_ML = auto() 146 | PYTHON_PYPY = auto() 147 | R = auto() 148 | RUBY = auto() 149 | RUST = auto() 150 | SCALA = auto() 151 | SQLITE = auto() 152 | SWIFT = auto() 153 | TYPESCRIPT = auto() 154 | VISUAL_BASIC = auto() 155 | 156 | 157 | class Flavor(IntEnum): 158 | """Flavor enumeration. 159 | 160 | Enumerates the flavors supported by Judge0 client. 161 | """ 162 | 163 | CE = 0 164 | EXTRA_CE = 1 165 | 166 | 167 | class Status(IntEnum): 168 | """Status enumeration. 169 | 170 | Enumerates possible status codes of a submission. 171 | """ 172 | 173 | IN_QUEUE = 1 174 | PROCESSING = 2 175 | ACCEPTED = 3 176 | WRONG_ANSWER = 4 177 | TIME_LIMIT_EXCEEDED = 5 178 | COMPILATION_ERROR = 6 179 | RUNTIME_ERROR_SIGSEGV = 7 180 | RUNTIME_ERROR_SIGXFSZ = 8 181 | RUNTIME_ERROR_SIGFPE = 9 182 | RUNTIME_ERROR_SIGABRT = 10 183 | RUNTIME_ERROR_NZEC = 11 184 | RUNTIME_ERROR_OTHER = 12 185 | INTERNAL_ERROR = 13 186 | EXEC_FORMAT_ERROR = 14 187 | 188 | def __str__(self): 189 | return self.name.lower().replace("_", " ").title() 190 | 191 | 192 | class Config(BaseModel): 193 | """Client config data model. 194 | 195 | Stores configuration data for the Judge0 client. 196 | """ 197 | 198 | allow_enable_network: bool 199 | allow_enable_per_process_and_thread_memory_limit: bool 200 | allow_enable_per_process_and_thread_time_limit: bool 201 | allowed_languages_for_compile_options: list[str] 202 | callbacks_max_tries: int 203 | callbacks_timeout: float 204 | cpu_extra_time: float 205 | cpu_time_limit: float 206 | enable_additional_files: bool 207 | enable_batched_submissions: bool 208 | enable_callbacks: bool 209 | enable_command_line_arguments: bool 210 | enable_compiler_options: bool 211 | enable_network: bool 212 | enable_per_process_and_thread_memory_limit: bool 213 | enable_per_process_and_thread_time_limit: bool 214 | enable_submission_delete: bool 215 | enable_wait_result: bool 216 | maintenance_mode: bool 217 | max_cpu_extra_time: float 218 | max_cpu_time_limit: float 219 | max_extract_size: int 220 | max_file_size: int 221 | max_max_file_size: int 222 | max_max_processes_and_or_threads: int 223 | max_memory_limit: int 224 | max_number_of_runs: int 225 | max_processes_and_or_threads: int 226 | max_queue_size: int 227 | max_stack_limit: int 228 | max_submission_batch_size: int 229 | max_wall_time_limit: float 230 | memory_limit: int 231 | number_of_runs: int 232 | redirect_stderr_to_stdout: bool 233 | stack_limit: int 234 | submission_cache_duration: float 235 | use_docs_as_homepage: bool 236 | wall_time_limit: float 237 | -------------------------------------------------------------------------------- /src/judge0/__init__.py: -------------------------------------------------------------------------------- 1 | import logging 2 | import os 3 | 4 | from typing import Union 5 | 6 | from .api import ( 7 | async_execute, 8 | async_run, 9 | execute, 10 | get_client, 11 | run, 12 | sync_execute, 13 | sync_run, 14 | wait, 15 | ) 16 | from .base_types import Flavor, Language, LanguageAlias, Status, TestCase 17 | from .clients import ( 18 | ATD, 19 | ATDJudge0CE, 20 | ATDJudge0ExtraCE, 21 | Client, 22 | Judge0Cloud, 23 | Judge0CloudCE, 24 | Judge0CloudExtraCE, 25 | Rapid, 26 | RapidJudge0CE, 27 | RapidJudge0ExtraCE, 28 | ) 29 | from .filesystem import File, Filesystem 30 | from .retry import MaxRetries, MaxWaitTime, RegularPeriodRetry 31 | from .submission import Submission 32 | 33 | __version__ = "0.1.0.dev0" 34 | 35 | __all__ = [ 36 | "ATD", 37 | "ATDJudge0CE", 38 | "ATDJudge0ExtraCE", 39 | "Client", 40 | "File", 41 | "Filesystem", 42 | "Judge0Cloud", 43 | "Judge0CloudCE", 44 | "Judge0CloudExtraCE", 45 | "Language", 46 | "LanguageAlias", 47 | "MaxRetries", 48 | "MaxWaitTime", 49 | "Rapid", 50 | "RapidJudge0CE", 51 | "RapidJudge0ExtraCE", 52 | "RegularPeriodRetry", 53 | "Status", 54 | "Submission", 55 | "TestCase", 56 | "async_execute", 57 | "async_run", 58 | "execute", 59 | "get_client", 60 | "run", 61 | "sync_execute", 62 | "sync_run", 63 | "wait", 64 | ] 65 | 66 | JUDGE0_IMPLICIT_CE_CLIENT = None 67 | JUDGE0_IMPLICIT_EXTRA_CE_CLIENT = None 68 | 69 | logger = logging.getLogger(__name__) 70 | suppress_preview_warning = os.getenv("JUDGE0_SUPPRESS_PREVIEW_WARNING") is not None 71 | 72 | 73 | def _get_implicit_client(flavor: Flavor) -> Client: 74 | global JUDGE0_IMPLICIT_CE_CLIENT, JUDGE0_IMPLICIT_EXTRA_CE_CLIENT 75 | 76 | # Implicit clients are already set. 77 | if flavor == Flavor.CE and JUDGE0_IMPLICIT_CE_CLIENT is not None: 78 | return JUDGE0_IMPLICIT_CE_CLIENT 79 | if flavor == Flavor.EXTRA_CE and JUDGE0_IMPLICIT_EXTRA_CE_CLIENT is not None: 80 | return JUDGE0_IMPLICIT_EXTRA_CE_CLIENT 81 | 82 | try: 83 | from dotenv import load_dotenv 84 | 85 | load_dotenv() 86 | except: # noqa: E722 87 | pass 88 | 89 | # Let's check if we can find a self-hosted client. 90 | client = _get_custom_client(flavor) 91 | 92 | # Try to find one of the API keys for hub clients. 93 | if client is None: 94 | client = _get_hub_client(flavor) 95 | 96 | # If we didn't find any of the possible keys, initialize 97 | # the preview client based on the flavor. 98 | if client is None: 99 | client = _get_preview_client(flavor) 100 | 101 | if flavor == Flavor.CE: 102 | JUDGE0_IMPLICIT_CE_CLIENT = client 103 | else: 104 | JUDGE0_IMPLICIT_EXTRA_CE_CLIENT = client 105 | 106 | return client 107 | 108 | 109 | def _get_preview_client(flavor: Flavor) -> Union[Judge0CloudCE, Judge0CloudExtraCE]: 110 | if not suppress_preview_warning: 111 | logger.warning( 112 | "You are using a preview version of the client which is not recommended" 113 | " for production.\n" 114 | "For production, please specify your API key in the environment variable." 115 | ) 116 | 117 | if flavor == Flavor.CE: 118 | return Judge0CloudCE() 119 | else: 120 | return Judge0CloudExtraCE() 121 | 122 | 123 | def _get_custom_client(flavor: Flavor) -> Union[Client, None]: 124 | from json import loads 125 | 126 | ce_endpoint = os.getenv("JUDGE0_CE_ENDPOINT") 127 | ce_auth_header = os.getenv("JUDGE0_CE_AUTH_HEADERS") 128 | extra_ce_endpoint = os.getenv("JUDGE0_EXTRA_CE_ENDPOINT") 129 | extra_ce_auth_header = os.getenv("JUDGE0_EXTRA_CE_AUTH_HEADERS") 130 | 131 | if flavor == Flavor.CE and ce_endpoint is not None and ce_auth_header is not None: 132 | return Client( 133 | endpoint=ce_endpoint, 134 | auth_headers=loads(ce_auth_header), 135 | ) 136 | 137 | if ( 138 | flavor == Flavor.EXTRA_CE 139 | and extra_ce_endpoint is not None 140 | and extra_ce_auth_header is not None 141 | ): 142 | return Client( 143 | endpoint=extra_ce_endpoint, 144 | auth_headers=loads(extra_ce_auth_header), 145 | ) 146 | 147 | return None 148 | 149 | 150 | def _get_hub_client(flavor: Flavor) -> Union[Client, None]: 151 | from .clients import CE, EXTRA_CE 152 | 153 | if flavor == Flavor.CE: 154 | client_classes = CE 155 | else: 156 | client_classes = EXTRA_CE 157 | 158 | for client_class in client_classes: 159 | api_key = os.getenv(client_class.API_KEY_ENV) 160 | if api_key is not None: 161 | client = client_class(api_key) 162 | break 163 | else: 164 | client = None 165 | 166 | return client 167 | 168 | 169 | CE = Flavor.CE 170 | EXTRA_CE = Flavor.EXTRA_CE 171 | 172 | ASSEMBLY = LanguageAlias.ASSEMBLY 173 | BASH = LanguageAlias.BASH 174 | BASIC = LanguageAlias.BASIC 175 | BOSQUE = LanguageAlias.BOSQUE 176 | C = LanguageAlias.C 177 | C3 = LanguageAlias.C3 178 | CLOJURE = LanguageAlias.CLOJURE 179 | COBOL = LanguageAlias.COBOL 180 | COMMON_LISP = LanguageAlias.COMMON_LISP 181 | CPP = LanguageAlias.CPP 182 | CPP_CLANG = LanguageAlias.CPP_CLANG 183 | CPP_GCC = LanguageAlias.CPP_GCC 184 | CPP_TEST = LanguageAlias.CPP_TEST 185 | CPP_TEST_CLANG = LanguageAlias.CPP_TEST_CLANG 186 | CPP_TEST_GCC = LanguageAlias.CPP_TEST_GCC 187 | CSHARP = LanguageAlias.CSHARP 188 | CSHARP_DOTNET = LanguageAlias.CSHARP_DOTNET 189 | CSHARP_MONO = LanguageAlias.CSHARP_MONO 190 | CSHARP_TEST = LanguageAlias.CSHARP_TEST 191 | C_CLANG = LanguageAlias.C_CLANG 192 | C_GCC = LanguageAlias.C_GCC 193 | D = LanguageAlias.D 194 | DART = LanguageAlias.DART 195 | ELIXIR = LanguageAlias.ELIXIR 196 | ERLANG = LanguageAlias.ERLANG 197 | EXECUTABLE = LanguageAlias.EXECUTABLE 198 | FORTRAN = LanguageAlias.FORTRAN 199 | FSHARP = LanguageAlias.FSHARP 200 | GO = LanguageAlias.GO 201 | GROOVY = LanguageAlias.GROOVY 202 | HASKELL = LanguageAlias.HASKELL 203 | JAVA = LanguageAlias.JAVA 204 | JAVAFX = LanguageAlias.JAVAFX 205 | JAVASCRIPT = LanguageAlias.JAVASCRIPT 206 | JAVA_JDK = LanguageAlias.JAVA_JDK 207 | JAVA_OPENJDK = LanguageAlias.JAVA_OPENJDK 208 | JAVA_TEST = LanguageAlias.JAVA_TEST 209 | KOTLIN = LanguageAlias.KOTLIN 210 | LUA = LanguageAlias.LUA 211 | MPI_C = LanguageAlias.MPI_C 212 | MPI_CPP = LanguageAlias.MPI_CPP 213 | MPI_PYTHON = LanguageAlias.MPI_PYTHON 214 | MULTI_FILE = LanguageAlias.MULTI_FILE 215 | NIM = LanguageAlias.NIM 216 | OBJECTIVE_C = LanguageAlias.OBJECTIVE_C 217 | OCAML = LanguageAlias.OCAML 218 | OCTAVE = LanguageAlias.OCTAVE 219 | PASCAL = LanguageAlias.PASCAL 220 | PERL = LanguageAlias.PERL 221 | PHP = LanguageAlias.PHP 222 | PLAIN_TEXT = LanguageAlias.PLAIN_TEXT 223 | PROLOG = LanguageAlias.PROLOG 224 | PYTHON = LanguageAlias.PYTHON 225 | PYTHON2 = LanguageAlias.PYTHON2 226 | PYTHON2_PYPY = LanguageAlias.PYTHON2_PYPY 227 | PYTHON3 = LanguageAlias.PYTHON3 228 | PYTHON3_PYPY = LanguageAlias.PYTHON3_PYPY 229 | PYTHON_FOR_ML = LanguageAlias.PYTHON_FOR_ML 230 | PYTHON_PYPY = LanguageAlias.PYTHON_PYPY 231 | R = LanguageAlias.R 232 | RUBY = LanguageAlias.RUBY 233 | RUST = LanguageAlias.RUST 234 | SCALA = LanguageAlias.SCALA 235 | SQLITE = LanguageAlias.SQLITE 236 | SWIFT = LanguageAlias.SWIFT 237 | TYPESCRIPT = LanguageAlias.TYPESCRIPT 238 | VISUAL_BASIC = LanguageAlias.VISUAL_BASIC 239 | -------------------------------------------------------------------------------- /tests/test_submission.py: -------------------------------------------------------------------------------- 1 | from base64 import b64decode 2 | 3 | from judge0 import run, Status, Submission, wait 4 | from judge0.base_types import LanguageAlias 5 | 6 | 7 | def test_from_json(): 8 | submission_dict = { 9 | "source_code": "cHJpbnQoJ0hlbGxvLCBXb3JsZCEnKQ==", 10 | "language_id": 100, 11 | "stdin": None, 12 | "expected_output": None, 13 | "stdout": "SGVsbG8sIFdvcmxkIQo=", 14 | "status_id": 3, 15 | "created_at": "2024-12-09T17:22:55.662Z", 16 | "finished_at": "2024-12-09T17:22:56.045Z", 17 | "time": "0.152", 18 | "memory": 13740, 19 | "stderr": None, 20 | "token": "5513d8ca-975b-4499-b54b-342f1952d00e", 21 | "number_of_runs": 1, 22 | "cpu_time_limit": "5.0", 23 | "cpu_extra_time": "1.0", 24 | "wall_time_limit": "10.0", 25 | "memory_limit": 128000, 26 | "stack_limit": 64000, 27 | "max_processes_and_or_threads": 60, 28 | "enable_per_process_and_thread_time_limit": False, 29 | "enable_per_process_and_thread_memory_limit": False, 30 | "max_file_size": 1024, 31 | "compile_output": None, 32 | "exit_code": 0, 33 | "exit_signal": None, 34 | "message": None, 35 | "wall_time": "0.17", 36 | "compiler_options": None, 37 | "command_line_arguments": None, 38 | "redirect_stderr_to_stdout": False, 39 | "callback_url": None, 40 | "additional_files": None, 41 | "enable_network": False, 42 | "post_execution_filesystem": "UEsDBBQACAAIANyKiVkAAAAAAAAAABYAAAAJABwAc" 43 | "2NyaXB0LnB5VVQJAANvJ1dncCdXZ3V4CwABBOgDAAAE6AMAACsoyswr0VD3SM3JyddRCM8v" 44 | "yklRVNcEAFBLBwgynNLKGAAAABYAAABQSwECHgMUAAgACADciolZMpzSyhgAAAAWAAAACQA" 45 | "YAAAAAAABAAAApIEAAAAAc2NyaXB0LnB5VVQFAANvJ1dndXgLAAEE6AMAAAToAwAAUEsFBg" 46 | "AAAAABAAEATwAAAGsAAAAAAA==", 47 | "status": {"id": 3, "description": "Accepted"}, 48 | "language": {"id": 100, "name": "Python (3.12.5)"}, 49 | } 50 | 51 | _ = Submission(**submission_dict) 52 | 53 | 54 | def test_status_before_and_after_submission(request): 55 | client = request.getfixturevalue("ce_client") 56 | submission = Submission( 57 | source_code='print("Hello World!")', language=LanguageAlias.PYTHON 58 | ) 59 | 60 | assert submission.status is None 61 | 62 | client.create_submission(submission) 63 | client.get_submission(submission) 64 | 65 | assert submission.status.__class__ == Status 66 | assert submission.status >= Status.IN_QUEUE 67 | 68 | 69 | def test_is_done(request): 70 | client = request.getfixturevalue("ce_client") 71 | submission = Submission( 72 | source_code='print("Hello World!")', language=LanguageAlias.PYTHON 73 | ) 74 | 75 | assert submission.status is None 76 | 77 | client.create_submission(submission) 78 | wait(client=client, submissions=submission) 79 | 80 | assert submission.is_done() 81 | 82 | 83 | def test_language_before_and_after_execution(request): 84 | client = request.getfixturevalue("ce_client") 85 | code = """\ 86 | public class Main { 87 | public static void main(String[] args) { 88 | System.out.println("Hello World"); 89 | } 90 | } 91 | """ 92 | 93 | submission = Submission( 94 | source_code=code, 95 | language=LanguageAlias.JAVA, 96 | ) 97 | 98 | assert submission.language == LanguageAlias.JAVA 99 | submission = run(client=client, submissions=submission) 100 | assert submission.language == LanguageAlias.JAVA 101 | 102 | 103 | def test_language_executable(request): 104 | client = request.getfixturevalue("ce_client") 105 | code = b64decode( 106 | "f0VMRgIBAQAAAAAAAAAAAAIAPgABAAAAAABAAAAAAABAAAAAAAAAAEAQAAAAAAAAAAAAAEAAOAABAEAABAADAAEAAAAFAAAAABAAAAAAAAAAAEAAAAAAAAAAQAAAAAAAJQAAAAAAAAAlAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADHAjVANsAG+GABAAInHDwUx/41HPA8FAGhlbGxvLCB3b3JsZAoALnNoc3RydGFiAC50ZXh0AC5yb2RhdGEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACwAAAAEAAAAGAAAAAAAAAAAAQAAAAAAAABAAAAAAAAAXAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAABEAAAABAAAAAgAAAAAAAAAYAEAAAAAAABgQAAAAAAAADQAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAABAAAAAwAAAAAAAAAAAAAAAAAAAAAAAAAlEAAAAAAAABkAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAA" # noqa: E501 107 | ) 108 | submission = Submission( 109 | source_code=code, 110 | language=LanguageAlias.EXECUTABLE, 111 | ) 112 | 113 | assert submission.language == LanguageAlias.EXECUTABLE 114 | submission = run(client=client, submissions=submission) 115 | assert submission.language == LanguageAlias.EXECUTABLE 116 | assert submission.stdout == "hello, world\n" 117 | -------------------------------------------------------------------------------- /tests/test_api_test_cases.py: -------------------------------------------------------------------------------- 1 | """Separate file containing tests related to test case functionality.""" 2 | 3 | import judge0 4 | import pytest 5 | from judge0.api import create_submissions_from_test_cases 6 | from judge0.base_types import LanguageAlias, Status, TestCase 7 | from judge0.submission import Submission 8 | 9 | 10 | @pytest.mark.parametrize( 11 | "test_case,expected_output", 12 | [ 13 | [ 14 | TestCase(input="input_1", expected_output="output_1"), 15 | TestCase(input="input_1", expected_output="output_1"), 16 | ], 17 | [ 18 | tuple([]), 19 | TestCase(input=None, expected_output=None), 20 | ], 21 | [ 22 | ("input_tuple",), 23 | TestCase(input="input_tuple", expected_output=None), 24 | ], 25 | [ 26 | ("input_tuple", "output_tuple"), 27 | TestCase(input="input_tuple", expected_output="output_tuple"), 28 | ], 29 | [ 30 | [], 31 | TestCase(input=None, expected_output=None), 32 | ], 33 | [ 34 | ["input_list"], 35 | TestCase(input="input_list", expected_output=None), 36 | ], 37 | [ 38 | ["input_list", "output_list"], 39 | TestCase(input="input_list", expected_output="output_list"), 40 | ], 41 | [ 42 | {"input": "input_dict", "expected_output": "output_dict"}, 43 | TestCase(input="input_dict", expected_output="output_dict"), 44 | ], 45 | [ 46 | None, 47 | None, 48 | ], 49 | ], 50 | ) 51 | def test_test_case_from_record(test_case, expected_output): 52 | assert TestCase.from_record(test_case) == expected_output 53 | 54 | 55 | @pytest.mark.parametrize( 56 | "submissions,test_cases,expected_type", 57 | [ 58 | [Submission(source_code=""), TestCase(), Submission], 59 | [[Submission(source_code="")], TestCase(), list], 60 | [Submission(source_code=""), [TestCase()], list], 61 | [[Submission(source_code="")], [TestCase()], list], 62 | ], 63 | ) 64 | def test_create_submissions_from_test_cases_return_type( 65 | submissions, test_cases, expected_type 66 | ): 67 | output = create_submissions_from_test_cases(submissions, test_cases) 68 | assert type(output) is expected_type 69 | 70 | 71 | class TestCreateSubmissionsFromTestCases: 72 | @pytest.mark.parametrize( 73 | "test_case,stdin,expected_output", 74 | [ 75 | [TestCase(), None, None], 76 | [[], None, None], 77 | [{}, None, None], 78 | [tuple([]), None, None], 79 | ], 80 | ) 81 | def test_empty_test_case(self, test_case, stdin, expected_output): 82 | submission = create_submissions_from_test_cases( 83 | Submission(), test_cases=test_case 84 | ) 85 | 86 | assert ( 87 | submission.stdin == stdin and submission.expected_output == expected_output 88 | ) 89 | 90 | @pytest.mark.parametrize( 91 | "test_case,stdin,expected_output", 92 | [ 93 | [TestCase(), None, None], 94 | [TestCase(input="input"), "input", None], 95 | [TestCase(expected_output="output"), None, "output"], 96 | [["input_list"], "input_list", None], 97 | [["input_list", "output_list"], "input_list", "output_list"], 98 | [{"input": "input_dict"}, "input_dict", None], 99 | [ 100 | {"input": "input_dict", "expected_output": "output_dict"}, 101 | "input_dict", 102 | "output_dict", 103 | ], 104 | [("input_tuple",), "input_tuple", None], 105 | [("input_tuple", "output_tuple"), "input_tuple", "output_tuple"], 106 | ], 107 | ) 108 | def test_single_test_case(self, test_case, stdin, expected_output): 109 | submission = create_submissions_from_test_cases( 110 | Submission(), test_cases=test_case 111 | ) 112 | 113 | assert ( 114 | submission.stdin == stdin and submission.expected_output == expected_output 115 | ) 116 | 117 | @pytest.mark.parametrize( 118 | "test_cases,stdin,expected_output", 119 | [ 120 | [[TestCase()], None, None], 121 | [[TestCase(input="input")], "input", None], 122 | [[TestCase(expected_output="output")], None, "output"], 123 | [(["input_list"],), "input_list", None], 124 | [(["input_list", "output_list"],), "input_list", "output_list"], 125 | [({"input": "input_dict"},), "input_dict", None], 126 | [ 127 | ({"input": "input_dict", "expected_output": "output_dict"},), 128 | "input_dict", 129 | "output_dict", 130 | ], 131 | [ 132 | [ 133 | ("input_tuple",), 134 | ], 135 | "input_tuple", 136 | None, 137 | ], 138 | [ 139 | [ 140 | ("input_tuple", "output_tuple"), 141 | ], 142 | "input_tuple", 143 | "output_tuple", 144 | ], 145 | ], 146 | ) 147 | def test_single_test_case_in_iterable(self, test_cases, stdin, expected_output): 148 | submissions = create_submissions_from_test_cases( 149 | Submission(), test_cases=test_cases 150 | ) 151 | 152 | for submission in submissions: 153 | assert ( 154 | submission.stdin == stdin 155 | and submission.expected_output == expected_output 156 | ) 157 | 158 | 159 | @pytest.mark.parametrize( 160 | "source_code_or_submissions,test_cases,expected_status", 161 | [ 162 | [ 163 | "print(f'Hello, {input()}')", 164 | [TestCase("Judge0", "Hello, Judge0")], 165 | [Status.ACCEPTED], 166 | ], 167 | [ 168 | "print(f'Hello, {input()}')", 169 | [ 170 | TestCase("Judge0", "Hello, Judge0"), 171 | TestCase("pytest", "Hello, pytest"), 172 | ], 173 | [Status.ACCEPTED, Status.ACCEPTED], 174 | ], 175 | [ 176 | Submission( 177 | source_code="print(f'Hello, {input()}')", 178 | language=LanguageAlias.PYTHON, 179 | ), 180 | [ 181 | TestCase("Judge0", "Hello, Judge0"), 182 | ], 183 | [Status.ACCEPTED], 184 | ], 185 | [ 186 | Submission( 187 | source_code="print(f'Hello, {input()}')", 188 | language=LanguageAlias.PYTHON, 189 | ), 190 | [ 191 | TestCase("Judge0", "Hello, Judge0"), 192 | TestCase("pytest", "Hi, pytest"), 193 | ], 194 | [Status.ACCEPTED, Status.WRONG_ANSWER], 195 | ], 196 | [ 197 | [ 198 | Submission( 199 | source_code="print(f'Hello, {input()}')", 200 | language=LanguageAlias.PYTHON, 201 | ), 202 | Submission( 203 | source_code="print(f'Hello, {input()}')", 204 | language=LanguageAlias.PYTHON, 205 | ), 206 | ], 207 | [ 208 | TestCase("Judge0", "Hello, Judge0"), 209 | TestCase("pytest", "Hello, pytest"), 210 | ], 211 | [ 212 | Status.ACCEPTED, 213 | Status.ACCEPTED, 214 | Status.WRONG_ANSWER, 215 | Status.WRONG_ANSWER, 216 | ], 217 | ], 218 | ], 219 | ) 220 | def test_test_cases_from_run( 221 | source_code_or_submissions, test_cases, expected_status, request 222 | ): 223 | client = request.getfixturevalue("ce_client") 224 | 225 | if isinstance(source_code_or_submissions, str): 226 | submissions = judge0.run( 227 | client=client, 228 | source_code=source_code_or_submissions, 229 | test_cases=test_cases, 230 | language=LanguageAlias.PYTHON, 231 | ) 232 | else: 233 | submissions = judge0.run( 234 | client=client, 235 | submissions=source_code_or_submissions, 236 | test_cases=test_cases, 237 | ) 238 | 239 | assert [submission.status for submission in submissions] == expected_status 240 | 241 | 242 | @pytest.mark.parametrize( 243 | "submissions,expected_status", 244 | [ 245 | [ 246 | Submission( 247 | source_code="print(f'Hello, {input()}')", 248 | language=LanguageAlias.PYTHON, 249 | stdin="Judge0", 250 | expected_output="Hello, Judge0", 251 | ), 252 | Status.ACCEPTED, 253 | ], 254 | [ 255 | [ 256 | Submission( 257 | source_code="print(f'Hello, {input()}')", 258 | language=LanguageAlias.PYTHON, 259 | stdin="Judge0", 260 | expected_output="Hello, Judge0", 261 | ), 262 | Submission( 263 | source_code="print(f'Hello, {input()}')", 264 | language=LanguageAlias.PYTHON, 265 | stdin="pytest", 266 | expected_output="Hello, pytest", 267 | ), 268 | ], 269 | [Status.ACCEPTED, Status.ACCEPTED], 270 | ], 271 | ], 272 | ) 273 | def test_no_test_cases(submissions, expected_status, request): 274 | client = request.getfixturevalue("ce_client") 275 | 276 | submissions = judge0.run( 277 | client=client, 278 | submissions=submissions, 279 | ) 280 | 281 | if isinstance(submissions, list): 282 | assert [submission.status for submission in submissions] == expected_status 283 | else: 284 | assert submissions.status == expected_status 285 | 286 | 287 | @pytest.mark.parametrize("n_submissions", [42, 84]) 288 | def test_batched_test_cases(n_submissions, request): 289 | client = request.getfixturevalue("ce_client") 290 | submissions = [ 291 | Submission( 292 | source_code=f"print({i})", 293 | language=LanguageAlias.PYTHON, 294 | expected_output=f"{i}", 295 | ) 296 | for i in range(n_submissions) 297 | ] 298 | 299 | results = judge0.run(client=client, submissions=submissions) 300 | 301 | assert len(results) == n_submissions 302 | assert all([result.status == Status.ACCEPTED for result in results]) 303 | -------------------------------------------------------------------------------- /src/judge0/submission.py: -------------------------------------------------------------------------------- 1 | import copy 2 | from datetime import datetime 3 | from typing import Any, Optional, Union 4 | 5 | from pydantic import BaseModel, ConfigDict, Field, field_validator, UUID4 6 | 7 | from .base_types import Iterable, LanguageAlias, Status 8 | from .common import decode, encode 9 | from .filesystem import Filesystem 10 | 11 | ENCODED_REQUEST_FIELDS = { 12 | "source_code", 13 | "additional_files", 14 | "stdin", 15 | "expected_output", 16 | } 17 | ENCODED_RESPONSE_FIELDS = { 18 | "stdout", 19 | "stderr", 20 | "compile_output", 21 | # "post_execution_filesystem", 22 | } 23 | ENCODED_FIELDS = ENCODED_REQUEST_FIELDS | ENCODED_RESPONSE_FIELDS 24 | EXTRA_REQUEST_FIELDS = { 25 | "compiler_options", 26 | "command_line_arguments", 27 | "cpu_time_limit", 28 | "cpu_extra_time", 29 | "wall_time_limit", 30 | "memory_limit", 31 | "stack_limit", 32 | "max_processes_and_or_threads", 33 | "enable_per_process_and_thread_time_limit", 34 | "enable_per_process_and_thread_memory_limit", 35 | "max_file_size", 36 | "redirect_stderr_to_stdout", 37 | "enable_network", 38 | "number_of_runs", 39 | "callback_url", 40 | } 41 | EXTRA_RESPONSE_FIELDS = { 42 | "message", 43 | "exit_code", 44 | "exit_signal", 45 | "status", 46 | "created_at", 47 | "finished_at", 48 | "token", 49 | "time", 50 | "wall_time", 51 | "memory", 52 | } 53 | REQUEST_FIELDS = ENCODED_REQUEST_FIELDS | EXTRA_REQUEST_FIELDS 54 | RESPONSE_FIELDS = ENCODED_RESPONSE_FIELDS | EXTRA_RESPONSE_FIELDS 55 | FIELDS = REQUEST_FIELDS | RESPONSE_FIELDS 56 | SKIP_FIELDS = {"language_id", "language", "status_id"} 57 | DATETIME_FIELDS = {"created_at", "finished_at"} 58 | FLOATING_POINT_FIELDS = { 59 | "cpu_time_limit", 60 | "cpu_extra_time", 61 | "time", 62 | "wall_time", 63 | "wall_time_limit", 64 | } 65 | 66 | Submissions = Iterable["Submission"] 67 | 68 | 69 | class Submission(BaseModel): 70 | """ 71 | Stores a representation of a Submission to/from Judge0. 72 | 73 | Parameters 74 | ---------- 75 | source_code : str, optional 76 | The source code to be executed. 77 | language : LanguageAlias or int, optional 78 | The programming language of the source code. Defaults to `LanguageAlias.PYTHON`. 79 | additional_files : base64 encoded string, optional 80 | Additional files that should be available alongside the source code. 81 | Value of this string should represent the content of a .zip that 82 | contains additional files. This attribute is required for multi-file 83 | programs. 84 | compiler_options : str, optional 85 | Options for the compiler (i.e. compiler flags). 86 | command_line_arguments : str, optional 87 | Command line arguments for the program. 88 | stdin : str, optional 89 | Input to be fed via standard input during execution. 90 | expected_output : str, optional 91 | The expected output of the program. 92 | cpu_time_limit : float, optional 93 | Maximum CPU time allowed for execution, in seconds. Time in which the 94 | OS assigns the processor to different tasks is not counted. Depends on 95 | configuration. 96 | cpu_extra_time : float, optional 97 | Additional CPU time allowance in case of time extension. Depends on 98 | configuration. 99 | wall_time_limit : float, optional 100 | Maximum wall clock time allowed for execution, in seconds. Depends on 101 | configuration. 102 | memory_limit : float, optional 103 | Maximum memory allocation allowed for the process, in kilobytes. 104 | Depends on configuration. 105 | stack_limit : int, optional 106 | Maximum stack size allowed, in kilobytes. Depends on configuration. 107 | max_processes_and_or_threads : int, optional 108 | Maximum number of processes and/or threads program can create. Depends 109 | on configuration. 110 | enable_per_process_and_thread_time_limit : bool, optional 111 | If True, enforces time limits per process/thread. Depends on 112 | configuration. 113 | enable_per_process_and_thread_memory_limit : bool, optional 114 | If True, enforces memory limits per process/thread. Depends on 115 | configuration. 116 | max_file_size : int, optional 117 | Maximum file size allowed for output files, in kilobytes. Depends on 118 | configuration. 119 | redirect_stderr_to_stdout : bool, optional 120 | If True, redirects standard error output to standard output. 121 | enable_network : bool, optional 122 | If True, enables network access during execution. 123 | number_of_runs : int, optional 124 | Number of times the code should be executed. 125 | callback_url : str, optional 126 | URL for a callback to report execution results or status. 127 | """ 128 | 129 | source_code: Optional[Union[str, bytes]] = Field(default=None, repr=True) 130 | language: Union[LanguageAlias, int] = Field( 131 | default=LanguageAlias.PYTHON_FOR_ML, 132 | repr=True, 133 | ) 134 | additional_files: Optional[Union[str, Filesystem]] = Field(default=None, repr=True) 135 | compiler_options: Optional[str] = Field(default=None, repr=True) 136 | command_line_arguments: Optional[str] = Field(default=None, repr=True) 137 | stdin: Optional[str] = Field(default=None, repr=True) 138 | expected_output: Optional[str] = Field(default=None, repr=True) 139 | cpu_time_limit: Optional[float] = Field(default=None, repr=True) 140 | cpu_extra_time: Optional[float] = Field(default=None, repr=True) 141 | wall_time_limit: Optional[float] = Field(default=None, repr=True) 142 | memory_limit: Optional[float] = Field(default=None, repr=True) 143 | stack_limit: Optional[int] = Field(default=None, repr=True) 144 | max_processes_and_or_threads: Optional[int] = Field(default=None, repr=True) 145 | enable_per_process_and_thread_time_limit: Optional[bool] = Field( 146 | default=None, repr=True 147 | ) 148 | enable_per_process_and_thread_memory_limit: Optional[bool] = Field( 149 | default=None, repr=True 150 | ) 151 | max_file_size: Optional[int] = Field(default=None, repr=True) 152 | redirect_stderr_to_stdout: Optional[bool] = Field(default=None, repr=True) 153 | enable_network: Optional[bool] = Field(default=None, repr=True) 154 | number_of_runs: Optional[int] = Field(default=None, repr=True) 155 | callback_url: Optional[str] = Field(default=None, repr=True) 156 | 157 | # Post-execution submission attributes. 158 | stdout: Optional[str] = Field(default=None, repr=True) 159 | stderr: Optional[str] = Field(default=None, repr=True) 160 | compile_output: Optional[str] = Field(default=None, repr=True) 161 | message: Optional[str] = Field(default=None, repr=True) 162 | exit_code: Optional[int] = Field(default=None, repr=True) 163 | exit_signal: Optional[int] = Field(default=None, repr=True) 164 | status: Optional[Status] = Field(default=None, repr=True) 165 | created_at: Optional[datetime] = Field(default=None, repr=True) 166 | finished_at: Optional[datetime] = Field(default=None, repr=True) 167 | token: Optional[UUID4] = Field(default=None, repr=True) 168 | time: Optional[float] = Field(default=None, repr=True) 169 | wall_time: Optional[float] = Field(default=None, repr=True) 170 | memory: Optional[float] = Field(default=None, repr=True) 171 | post_execution_filesystem: Optional[Filesystem] = Field(default=None, repr=True) 172 | 173 | model_config = ConfigDict(extra="ignore") 174 | 175 | @field_validator(*ENCODED_FIELDS, mode="before") 176 | @classmethod 177 | def process_encoded_fields(cls, value: str) -> Optional[str]: 178 | """Validate all encoded attributes.""" 179 | if value is None: 180 | return None 181 | else: 182 | try: 183 | return decode(value) 184 | except Exception: 185 | return value 186 | 187 | @field_validator("post_execution_filesystem", mode="before") 188 | @classmethod 189 | def process_post_execution_filesystem(cls, content: str) -> Filesystem: 190 | """Validate post_execution_filesystem attribute.""" 191 | return Filesystem(content=content) 192 | 193 | @field_validator("status", mode="before") 194 | @classmethod 195 | def process_status(cls, value: dict) -> Status: 196 | """Validate status attribute.""" 197 | return Status(value["id"]) 198 | 199 | @field_validator("language", mode="before") 200 | @classmethod 201 | def process_language( 202 | cls, value: Union[LanguageAlias, dict] 203 | ) -> Union[LanguageAlias, int]: 204 | """Validate status attribute.""" 205 | if isinstance(value, dict): 206 | return value["id"] 207 | else: 208 | return value 209 | 210 | def set_attributes(self, attributes: dict[str, Any]) -> None: 211 | """Set submissions attributes while taking into account different 212 | attribute's types. 213 | 214 | Parameters 215 | ---------- 216 | attributes : dict 217 | Key-value pairs of Submission attributes and the corresponding 218 | value. 219 | """ 220 | for attr, value in attributes.items(): 221 | if attr in SKIP_FIELDS: 222 | continue 223 | 224 | if attr in ENCODED_FIELDS: 225 | value = decode(value) if value else None 226 | elif attr == "status": 227 | value = Status(value["id"]) 228 | elif attr in DATETIME_FIELDS and value is not None: 229 | value = datetime.strptime(value, "%Y-%m-%dT%H:%M:%S.%fZ") 230 | elif attr in FLOATING_POINT_FIELDS and value is not None: 231 | value = float(value) 232 | elif attr == "post_execution_filesystem": 233 | value = Filesystem(content=value) 234 | 235 | setattr(self, attr, value) 236 | 237 | def as_body(self, client: "Client") -> dict: 238 | """Prepare Submission as a dictionary while taking into account 239 | the client's restrictions. 240 | """ 241 | body = { 242 | "source_code": encode(self.source_code), 243 | "language_id": client.get_language_id(self.language), 244 | } 245 | 246 | for field in ENCODED_REQUEST_FIELDS: 247 | value = getattr(self, field) 248 | if value is not None: 249 | body[field] = encode(value) 250 | 251 | for field in EXTRA_REQUEST_FIELDS: 252 | value = getattr(self, field) 253 | if value is not None: 254 | body[field] = value 255 | 256 | return body 257 | 258 | def is_done(self) -> bool: 259 | """Check if submission is finished processing. 260 | 261 | Submission is considered finished if the submission status is not 262 | IN_QUEUE and not PROCESSING. 263 | """ 264 | if self.status is None: 265 | return False 266 | else: 267 | return self.status not in (Status.IN_QUEUE, Status.PROCESSING) 268 | 269 | def pre_execution_copy(self) -> "Submission": 270 | """Create a deep copy of a submission.""" 271 | new_submission = Submission() 272 | for attr in REQUEST_FIELDS: 273 | setattr(new_submission, attr, copy.deepcopy(getattr(self, attr))) 274 | new_submission.language = self.language 275 | return new_submission 276 | 277 | def __iter__(self): 278 | if self.post_execution_filesystem is None: 279 | return iter([]) 280 | else: 281 | return iter(self.post_execution_filesystem) 282 | -------------------------------------------------------------------------------- /src/judge0/api.py: -------------------------------------------------------------------------------- 1 | from typing import Optional, Union 2 | 3 | from .base_types import Flavor, Iterable, TestCase, TestCases, TestCaseType 4 | from .clients import Client 5 | from .common import batched 6 | from .errors import ClientResolutionError 7 | from .retry import RegularPeriodRetry, RetryStrategy 8 | from .submission import Submission, Submissions 9 | 10 | 11 | def get_client(flavor: Flavor = Flavor.CE) -> Client: 12 | """Resolve client from API keys from environment or default to preview client. 13 | 14 | Parameters 15 | ---------- 16 | flavor : Flavor 17 | Flavor of Judge0 Client. 18 | 19 | Returns 20 | ------- 21 | Client 22 | An object of base type Client and the specified flavor. 23 | """ 24 | from . import _get_implicit_client 25 | 26 | if isinstance(flavor, Flavor): 27 | return _get_implicit_client(flavor=flavor) 28 | else: 29 | raise ValueError( 30 | "Expected argument flavor to be of of type enum Flavor, " 31 | f"got {type(flavor)}." 32 | ) 33 | 34 | 35 | def _resolve_client( 36 | client: Optional[Union[Client, Flavor]] = None, 37 | submissions: Optional[Union[Submission, Submissions]] = None, 38 | ) -> Client: 39 | """Resolve a client from flavor or submission(s) arguments. 40 | 41 | Parameters 42 | ---------- 43 | client : Client or Flavor, optional 44 | A Client object or flavor of client. Returns the client if not None. 45 | submissions: Submission or Submissions, optional 46 | Submission(s) used to determine the suitable client. 47 | 48 | Returns 49 | ------- 50 | Client 51 | An object of base type Client. 52 | 53 | Raises 54 | ------ 55 | ClientResolutionError 56 | If there is no implemented client that supports all the languages specified 57 | in the submissions. 58 | """ 59 | # User explicitly passed a client. 60 | if isinstance(client, Client): 61 | return client 62 | 63 | # NOTE: At the moment, we do not support the option to check if explicit 64 | # flavor of a client supports the submissions, i.e. submissions argument is 65 | # ignored if flavor argument is provided. 66 | 67 | if isinstance(client, Flavor): 68 | return get_client(client) 69 | 70 | if client is None: 71 | if ( 72 | isinstance(submissions, Iterable) and len(submissions) == 0 73 | ) or submissions is None: 74 | raise ValueError("Client cannot be determined from empty submissions.") 75 | 76 | # client is None and we have to determine a flavor of the client from the 77 | # the submission's languages. 78 | if isinstance(submissions, Submission): 79 | submissions = [submissions] 80 | 81 | # Check which client supports all languages from the provided submissions. 82 | languages = [submission.language for submission in submissions] 83 | 84 | for flavor in Flavor: 85 | client = get_client(flavor) 86 | if client is not None and all( 87 | (client.is_language_supported(lang) for lang in languages) 88 | ): 89 | return client 90 | 91 | raise ClientResolutionError( 92 | "Failed to resolve the client from submissions argument. " 93 | "None of the implicit clients supports all languages from the submissions. " 94 | "Please explicitly provide the client argument." 95 | ) 96 | 97 | 98 | def create_submissions( 99 | *, 100 | client: Optional[Union[Client, Flavor]] = None, 101 | submissions: Optional[Union[Submission, Submissions]] = None, 102 | ) -> Union[Submission, Submissions]: 103 | """Universal function for creating submissions to the client. 104 | 105 | Parameters 106 | ---------- 107 | client : Client or Flavor, optional 108 | A client or client flavor where submissions should be created. 109 | submissions: Submission or Submissions, optional 110 | Submission(s) to create. 111 | 112 | Raises 113 | ------ 114 | ClientResolutionError 115 | Raised if client resolution fails. 116 | """ 117 | client = _resolve_client(client=client, submissions=submissions) 118 | 119 | if isinstance(submissions, Submission): 120 | return client.create_submission(submissions) 121 | 122 | result_submissions = [] 123 | for submission_batch in batched( 124 | submissions, client.config.max_submission_batch_size 125 | ): 126 | if len(submission_batch) > 1: 127 | result_submissions.extend(client.create_submissions(submission_batch)) 128 | else: 129 | result_submissions.append(client.create_submission(submission_batch[0])) 130 | 131 | return result_submissions 132 | 133 | 134 | def get_submissions( 135 | *, 136 | client: Optional[Union[Client, Flavor]] = None, 137 | submissions: Optional[Union[Submission, Submissions]] = None, 138 | fields: Optional[Union[str, Iterable[str]]] = None, 139 | ) -> Union[Submission, Submissions]: 140 | """Get submission (status) from a client. 141 | 142 | Parameters 143 | ---------- 144 | client : Client or Flavor, optional 145 | A client or client flavor where submissions should be checked. 146 | submissions : Submission or Submissions, optional 147 | Submission(s) to update. 148 | fields : str or sequence of str, optional 149 | Submission attributes that need to be updated. Defaults to all attributes. 150 | 151 | Raises 152 | ------ 153 | ClientResolutionError 154 | Raised if client resolution fails. 155 | """ 156 | client = _resolve_client(client=client, submissions=submissions) 157 | 158 | if isinstance(submissions, Submission): 159 | return client.get_submission(submissions, fields=fields) 160 | 161 | result_submissions = [] 162 | for submission_batch in batched( 163 | submissions, client.config.max_submission_batch_size 164 | ): 165 | if len(submission_batch) > 1: 166 | result_submissions.extend( 167 | client.get_submissions(submission_batch, fields=fields) 168 | ) 169 | else: 170 | result_submissions.append( 171 | client.get_submission(submission_batch[0], fields=fields) 172 | ) 173 | 174 | return result_submissions 175 | 176 | 177 | def wait( 178 | *, 179 | client: Optional[Union[Client, Flavor]] = None, 180 | submissions: Optional[Union[Submission, Submissions]] = None, 181 | retry_strategy: Optional[RetryStrategy] = None, 182 | ) -> Union[Submission, Submissions]: 183 | """Wait for all the submissions to finish. 184 | 185 | Parameters 186 | ---------- 187 | client : Client or Flavor, optional 188 | A client or client flavor where submissions should be checked. 189 | submissions : Submission or Submissions 190 | Submission(s) to wait for. 191 | retry_strategy : RetryStrategy, optional 192 | A retry strategy. 193 | 194 | Returns 195 | ------- 196 | Submission or Submissions 197 | A single submission or a list of submissions. 198 | 199 | Raises 200 | ------ 201 | ClientResolutionError 202 | Raised if client resolution fails. 203 | """ 204 | client = _resolve_client(client, submissions) 205 | 206 | if retry_strategy is None: 207 | if client.retry_strategy is None: 208 | retry_strategy = RegularPeriodRetry() 209 | else: 210 | retry_strategy = client.retry_strategy 211 | 212 | if isinstance(submissions, Submission): 213 | submissions_list = [submissions] 214 | else: 215 | submissions_list = submissions 216 | 217 | submissions_to_check = { 218 | submission.token: submission for submission in submissions_list 219 | } 220 | 221 | while len(submissions_to_check) > 0 and not retry_strategy.is_done(): 222 | get_submissions(client=client, submissions=list(submissions_to_check.values())) 223 | finished_submissions = [ 224 | token 225 | for token, submission in submissions_to_check.items() 226 | if submission.is_done() 227 | ] 228 | for token in finished_submissions: 229 | submissions_to_check.pop(token) 230 | 231 | # Don't wait if there is no submissions to check for anymore. 232 | if len(submissions_to_check) == 0: 233 | break 234 | 235 | retry_strategy.wait() 236 | retry_strategy.step() 237 | 238 | return submissions 239 | 240 | 241 | def create_submissions_from_test_cases( 242 | submissions: Union[Submission, Submissions], 243 | test_cases: Optional[Union[TestCaseType, TestCases]] = None, 244 | ) -> Union[Submission, list[Submission]]: 245 | """Create submissions from the submission and test case pairs. 246 | 247 | Function always returns a deep copy so make sure you are using the 248 | returned submission(s). 249 | 250 | Parameters 251 | ---------- 252 | submissions : Submission or Submissions 253 | Base submission(s) that need to be expanded with test cases. 254 | test_cases: TestCaseType or TestCases 255 | Test cases. 256 | 257 | Returns 258 | ------- 259 | Submissions or Submissions 260 | A single submission if submissions arguments is of type Submission or 261 | source_code argument is provided, and test_cases argument is of type 262 | TestCase. Otherwise returns a list of submissions. 263 | """ 264 | if isinstance(submissions, Submission): 265 | submissions_list = [submissions] 266 | else: 267 | submissions_list = submissions 268 | 269 | if isinstance(test_cases, TestCase) or test_cases is None: 270 | test_cases_list = [test_cases] 271 | multiple_test_cases = False 272 | else: 273 | try: 274 | # Let's assume that we are dealing with multiple test_cases that 275 | # can be created from test_cases argument. If this fails, i.e. 276 | # raises a ValueError, we know we are dealing with a test_cases=dict, 277 | # or test_cases=["in", "out"], or test_cases=tuple("in", "out"). 278 | test_cases_list = [TestCase.from_record(tc) for tc in test_cases] 279 | 280 | # It is possible to send test_cases={}, or test_cases=[], or 281 | # test_cases=tuple([]). In this case, we are treating that as None. 282 | if len(test_cases) > 0: 283 | multiple_test_cases = True 284 | else: 285 | multiple_test_cases = False 286 | test_cases_list = [None] 287 | except ValueError: 288 | test_cases_list = [test_cases] 289 | multiple_test_cases = False 290 | 291 | test_cases_list = [TestCase.from_record(test_case=tc) for tc in test_cases_list] 292 | 293 | all_submissions = [] 294 | for submission in submissions_list: 295 | for test_case in test_cases_list: 296 | submission_copy = submission.pre_execution_copy() 297 | if test_case is not None: 298 | submission_copy.stdin = test_case.input 299 | submission_copy.expected_output = test_case.expected_output 300 | all_submissions.append(submission_copy) 301 | 302 | if isinstance(submissions, Submission) and (not multiple_test_cases): 303 | return all_submissions[0] 304 | else: 305 | return all_submissions 306 | 307 | 308 | def _execute( 309 | *, 310 | client: Optional[Union[Client, Flavor]] = None, 311 | submissions: Optional[Union[Submission, Submissions]] = None, 312 | source_code: Optional[str] = None, 313 | test_cases: Optional[Union[TestCaseType, TestCases]] = None, 314 | wait_for_result: bool = False, 315 | **kwargs, 316 | ) -> Union[Submission, Submissions]: 317 | 318 | if submissions is not None and source_code is not None: 319 | raise ValueError( 320 | "Both submissions and source_code arguments are provided. " 321 | "Provide only one of the two." 322 | ) 323 | if submissions is None and source_code is None: 324 | raise ValueError("Neither source_code nor submissions argument are provided.") 325 | 326 | # Internally, let's rely on Submission's dataclass. 327 | if source_code is not None: 328 | submissions = Submission(source_code=source_code, **kwargs) 329 | 330 | client = _resolve_client(client=client, submissions=submissions) 331 | all_submissions = create_submissions_from_test_cases(submissions, test_cases) 332 | all_submissions = create_submissions(client=client, submissions=all_submissions) 333 | 334 | if wait_for_result: 335 | return wait(client=client, submissions=all_submissions) 336 | else: 337 | return all_submissions 338 | 339 | 340 | def async_execute( 341 | *, 342 | client: Optional[Union[Client, Flavor]] = None, 343 | submissions: Optional[Union[Submission, Submissions]] = None, 344 | source_code: Optional[str] = None, 345 | test_cases: Optional[Union[TestCaseType, TestCases]] = None, 346 | **kwargs, 347 | ) -> Union[Submission, Submissions]: 348 | """Create submission(s). 349 | 350 | Aliases: `async_run`. 351 | 352 | Parameters 353 | ---------- 354 | client : Client or Flavor, optional 355 | A client where submissions should be created. If None, will try to be 356 | resolved. 357 | submissions : Submission or Submissions, optional 358 | Submission or submissions for execution. 359 | source_code : str, optional 360 | A source code of a program. 361 | test_cases : TestCaseType or TestCases, optional 362 | A single test or a list of test cases 363 | **kwargs : dict 364 | Additional keyword arguments to pass to the Submission constructor. 365 | 366 | Returns 367 | ------- 368 | Submission or Submissions 369 | A single submission if submissions arguments is of type Submission or 370 | source_code argument is provided, and test_cases argument is of type 371 | TestCase. Otherwise returns a list of submissions. 372 | 373 | Raises 374 | ------ 375 | ClientResolutionError 376 | If client cannot be resolved from the submissions or the flavor. 377 | ValueError 378 | If both or neither submissions and source_code arguments are provided. 379 | """ 380 | return _execute( 381 | client=client, 382 | submissions=submissions, 383 | source_code=source_code, 384 | test_cases=test_cases, 385 | wait_for_result=False, 386 | **kwargs, 387 | ) 388 | 389 | 390 | def sync_execute( 391 | *, 392 | client: Optional[Union[Client, Flavor]] = None, 393 | submissions: Optional[Union[Submission, Submissions]] = None, 394 | source_code: Optional[str] = None, 395 | test_cases: Optional[Union[TestCaseType, TestCases]] = None, 396 | **kwargs, 397 | ) -> Union[Submission, Submissions]: 398 | """Create submission(s) and wait for their finish. 399 | 400 | Aliases: `execute`, `run`, `sync_run`. 401 | 402 | Parameters 403 | ---------- 404 | client : Client or Flavor, optional 405 | A client where submissions should be created. If None, will try to be 406 | resolved. 407 | submissions : Submission or Submissions, optional 408 | Submission(s) for execution. 409 | source_code: str, optional 410 | A source code of a program. 411 | test_cases: TestCaseType or TestCases, optional 412 | A single test or a list of test cases 413 | **kwargs : dict 414 | Additional keyword arguments to pass to the Submission constructor. 415 | 416 | Returns 417 | ------- 418 | Submission or Submissions 419 | A single submission if submissions arguments is of type Submission or 420 | source_code argument is provided, and test_cases argument is of type 421 | TestCase. Otherwise returns a list of submissions. 422 | 423 | Raises 424 | ------ 425 | ClientResolutionError 426 | If client cannot be resolved from the submissions or the flavor. 427 | ValueError 428 | If both or neither submissions and source_code arguments are provided. 429 | """ 430 | return _execute( 431 | client=client, 432 | submissions=submissions, 433 | source_code=source_code, 434 | wait_for_result=True, 435 | test_cases=test_cases, 436 | **kwargs, 437 | ) 438 | 439 | 440 | execute = sync_execute 441 | run = sync_execute 442 | sync_run = sync_execute 443 | async_run = async_execute 444 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Judge0 Python SDK 2 | 3 | The official Python SDK for Judge0. 4 | ```python 5 | >>> import judge0 6 | >>> result = judge0.run(source_code="print('hello, world')") 7 | >>> result.stdout 8 | 'hello, world\n' 9 | >>> result.time 10 | 0.987 11 | >>> result.memory 12 | 52440 13 | >>> for f in result: 14 | ... f.name 15 | ... f.content 16 | ... 17 | 'script.py' 18 | b"print('hello, world')" 19 | ``` 20 | 21 | ## Installation 22 | 23 | ```bash 24 | pip install judge0 25 | ``` 26 | 27 | ### Requirements 28 | 29 | - Python 3.10+ 30 | 31 | ## Quick Start 32 | 33 | ### Getting The API Key 34 | 35 | Get your API key from [Rapid](https://rapidapi.com/organization/judge0), or [ATD](https://www.allthingsdev.co/publisher/profile/Herman%20Zvonimir%20Do%C5%A1ilovi%C4%87). 36 | 37 | #### Notes 38 | 39 | * Judge0 has two flavors: Judge0 CE and Judge0 Extra CE, and their difference is just in the languages they support. When choosing Rapid and ATD you will need to explicitly subscribe to both flavors if you want to use both. 40 | 41 | ### Using Your API Key 42 | 43 | #### Option 1: Explicit Client Object 44 | 45 | Explicitly create a client object with your API key and pass it to Judge0 Python SDK functions. 46 | 47 | ```python 48 | import judge0 49 | client = judge0.RapidJudge0CE(api_key="xxx") 50 | result = judge0.run(client=client, source_code="print('hello, world')") 51 | print(result.stdout) 52 | ``` 53 | 54 | Other options include: 55 | - `judge0.RapidJudge0CE` 56 | - `judge0.ATDJudge0CE` 57 | - `judge0.RapidJudge0ExtraCE` 58 | - `judge0.ATDJudge0ExtraCE` 59 | 60 | #### Option 2: Implicit Client Object 61 | 62 | Put your API key in one of the following environment variables, respectable to the provider that issued you the API key: `JUDGE0_RAPID_API_KEY`, or `JUDGE0_ATD_API_KEY`. 63 | 64 | Judge0 Python SDK will automatically detect the environment variable and use it to create a client object that will be used for all API calls if you do not explicitly pass a client object. 65 | 66 | ```python 67 | import judge0 68 | result = judge0.run(source_code="print('hello, world')") 69 | print(result.stdout) 70 | ``` 71 | 72 | ## Examples 73 | ### hello, world 74 | 75 | ```python 76 | import judge0 77 | result = judge0.run(source_code="print('hello, world')", language=judge0.PYTHON) 78 | print(result.stdout) 79 | ``` 80 | 81 | ### Running C Programming Language 82 | 83 | ```python 84 | import judge0 85 | 86 | source_code = """ 87 | #include 88 | 89 | int main() { 90 | printf("hello, world\\n"); 91 | return 0; 92 | } 93 | """ 94 | 95 | result = judge0.run(source_code=source_code, language=judge0.C) 96 | print(result.stdout) 97 | ``` 98 | 99 | ### Running Java Programming Language 100 | 101 | ```python 102 | import judge0 103 | 104 | source_code = """ 105 | public class Main { 106 | public static void main(String[] args) { 107 | System.out.println("hello, world"); 108 | } 109 | } 110 | """ 111 | 112 | result = judge0.run(source_code=source_code, language=judge0.JAVA) 113 | print(result.stdout) 114 | ``` 115 | 116 | ### Reading From Standard Input 117 | 118 | ```python 119 | import judge0 120 | 121 | source_code = """ 122 | #include 123 | 124 | int main() { 125 | int a, b; 126 | scanf("%d %d", &a, &b); 127 | printf("%d\\n", a + b); 128 | 129 | char name[10]; 130 | scanf("%s", name); 131 | printf("Hello, %s!\\n", name); 132 | 133 | return 0; 134 | } 135 | """ 136 | 137 | stdin = """ 138 | 3 5 139 | Bob 140 | """ 141 | 142 | result = judge0.run(source_code=source_code, stdin=stdin, language=judge0.C) 143 | print(result.stdout) 144 | ``` 145 | 146 | ### Test Cases 147 | 148 | ```python 149 | import judge0 150 | 151 | results = judge0.run( 152 | source_code="print(f'Hello, {input()}!')", 153 | test_cases=[ 154 | ("Bob", "Hello, Bob!"), # Test Case #1. Tuple with first value as standard input, second value as expected output. 155 | { # Test Case #2. Dictionary with "input" and "expected_output" keys. 156 | "input": "Alice", 157 | "expected_output": "Hello, Alice!" 158 | }, 159 | ["Charlie", "Hello, Charlie!"], # Test Case #3. List with first value as standard input and second value as expected output. 160 | ], 161 | ) 162 | 163 | for i, result in enumerate(results): 164 | print(f"--- Test Case #{i + 1} ---") 165 | print(result.stdout) 166 | print(result.status) 167 | ``` 168 | 169 | ### Test Cases And Multiple Languages 170 | 171 | ```python 172 | import judge0 173 | 174 | submissions = [ 175 | judge0.Submission( 176 | source_code="print(f'Hello, {input()}!')", 177 | language=judge0.PYTHON, 178 | ), 179 | judge0.Submission( 180 | source_code=""" 181 | #include 182 | 183 | int main() { 184 | char name[10]; 185 | scanf("%s", name); 186 | printf("Hello, %s!\\n", name); 187 | return 0; 188 | } 189 | """, 190 | language=judge0.C, 191 | ), 192 | ] 193 | 194 | test_cases=[ 195 | ("Bob", "Hello, Bob!"), 196 | ("Alice", "Hello, Alice!"), 197 | ("Charlie", "Hello, Charlie!"), 198 | ] 199 | 200 | results = judge0.run(submissions=submissions, test_cases=test_cases) 201 | 202 | for i in range(len(submissions)): 203 | print(f"--- Submission #{i + 1} ---") 204 | 205 | for j in range(len(test_cases)): 206 | result = results[i * len(test_cases) + j] 207 | 208 | print(f"--- Test Case #{j + 1} ---") 209 | print(result.stdout) 210 | print(result.status) 211 | ``` 212 | 213 | ### Asynchronous Execution 214 | 215 | ```python 216 | import judge0 217 | 218 | submission = judge0.async_run(source_code="print('hello, world')") 219 | print(submission.stdout) # Prints 'None' 220 | 221 | judge0.wait(submissions=submission) # Wait for the submission to finish. 222 | 223 | print(submission.stdout) # Prints 'hello, world' 224 | ``` 225 | 226 | ### Get Languages 227 | 228 | ```python 229 | import judge0 230 | client = judge0.get_client() 231 | print(client.get_languages()) 232 | ``` 233 | 234 | ### Running LLM-Generated Code 235 | 236 | #### Simple Example With Ollama 237 | 238 | ```python 239 | # pip install judge0 ollama 240 | import os 241 | 242 | from ollama import Client 243 | import judge0 244 | 245 | # Get your free tier Ollama Cloud API key at https://ollama.com. 246 | client = Client( 247 | host="https://ollama.com", 248 | headers={"Authorization": "Bearer " + os.environ.get("OLLAMA_API_KEY")}, 249 | ) 250 | 251 | system = """ 252 | You are a helpful assistant that can execute code written in the C programming language. 253 | Only respond with the code written in the C programming language that needs to be executed and nothing else. 254 | Strip the backticks in code blocks. 255 | """ 256 | prompt = "How many r's are in the word 'strawberry'?" 257 | 258 | response = client.chat( 259 | model="gpt-oss:120b-cloud", 260 | messages=[ 261 | {"role": "system", "content": system}, 262 | {"role": "user", "content": prompt}, 263 | ], 264 | ) 265 | 266 | code = response["message"]["content"] 267 | print(f"CODE GENERATED BY THE MODEL:\n{code}\n") 268 | 269 | result = judge0.run(source_code=code, language=judge0.C) 270 | print(f"CODE EXECUTION RESULT:\n{result.stdout}") 271 | ``` 272 | 273 | #### Tool Calling (a.k.a. Function Calling) With Ollama 274 | 275 | ```python 276 | # pip install judge0 ollama 277 | import os 278 | 279 | from ollama import Client 280 | import judge0 281 | 282 | # Get your free tier Ollama Cloud API key at https://ollama.com. 283 | client = Client( 284 | host="https://ollama.com", 285 | headers={"Authorization": "Bearer " + os.environ.get("OLLAMA_API_KEY")}, 286 | ) 287 | 288 | model="qwen3-coder:480b-cloud" 289 | 290 | messages=[ 291 | {"role": "user", "content": "How many r's are in the word 'strawberry'?"}, 292 | ] 293 | 294 | tools = [{ 295 | "type": "function", 296 | "function": { 297 | "name": "execute_c", 298 | "description": "Execute the C programming language code.", 299 | "parameters": { 300 | "type": "object", 301 | "properties": { 302 | "code": { 303 | "type": "string", 304 | "description": "The code written in the C programming language." 305 | } 306 | }, 307 | "required": ["code"] 308 | } 309 | } 310 | }] 311 | 312 | response = client.chat(model=model, messages=messages, tools=tools) 313 | 314 | response_message = response["message"] 315 | messages.append(response_message) 316 | 317 | if response_message.tool_calls: 318 | for tool_call in response_message.tool_calls: 319 | if tool_call.function.name == "execute_c": 320 | code = tool_call.function.arguments["code"] 321 | print(f"CODE GENERATED BY THE MODEL:\n{code}\n") 322 | 323 | result = judge0.run(source_code=code, language=judge0.C) 324 | print(f"CODE EXECUTION RESULT:\n{result.stdout}\n") 325 | 326 | messages.append({ 327 | "role": "tool", 328 | "tool_name": "execute_c", 329 | "content": result.stdout, 330 | }) 331 | 332 | final_response = client.chat(model=model, messages=messages) 333 | print(f'FINAL RESPONSE BY THE MODEL:\n{final_response["message"]["content"]}') 334 | ``` 335 | 336 | #### Multi-Agent System For Iterative Code Generation, Execution, And Debugging 337 | 338 | ```python 339 | # pip install judge0 ag2[openai] 340 | import os 341 | from typing import Annotated, Optional 342 | 343 | from autogen import ConversableAgent, LLMConfig, register_function 344 | from autogen.tools import Tool 345 | from pydantic import BaseModel, Field 346 | import judge0 347 | 348 | 349 | class PythonCodeExecutionTool(Tool): 350 | def __init__(self) -> None: 351 | class CodeExecutionRequest(BaseModel): 352 | code: Annotated[str, Field(description="Python code to execute")] 353 | 354 | async def execute_python_code( 355 | code_execution_request: CodeExecutionRequest, 356 | ) -> Optional[str]: 357 | result = judge0.run( 358 | source_code=code_execution_request.code, 359 | language=judge0.PYTHON, 360 | redirect_stderr_to_stdout=True, 361 | ) 362 | return result.stdout 363 | 364 | super().__init__( 365 | name="python_execute_code", 366 | description="Executes Python code and returns the result.", 367 | func_or_tool=execute_python_code, 368 | ) 369 | 370 | 371 | python_executor = PythonCodeExecutionTool() 372 | 373 | # Get your free tier Ollama Cloud API key at https://ollama.com. 374 | llm_config = LLMConfig( 375 | { 376 | "api_type": "openai", 377 | "base_url": "https://ollama.com/v1", 378 | "api_key": os.environ.get("OLLAMA_API_KEY"), 379 | "model": "qwen3-coder:480b-cloud", 380 | } 381 | ) 382 | 383 | code_runner = ConversableAgent( 384 | name="code_runner", 385 | system_message="You are a code executor agent, when you don't execute code write the message 'TERMINATE' by itself.", 386 | human_input_mode="NEVER", 387 | llm_config=llm_config, 388 | ) 389 | 390 | question_agent = ConversableAgent( 391 | name="question_agent", 392 | system_message=( 393 | "You are a developer AI agent. " 394 | "Send all your code suggestions to the python_executor tool where it will be executed and result returned to you. " 395 | "Keep refining the code until it works." 396 | ), 397 | llm_config=llm_config, 398 | ) 399 | 400 | register_function( 401 | python_executor, 402 | caller=question_agent, 403 | executor=code_runner, 404 | description="Run Python code", 405 | ) 406 | 407 | result = code_runner.initiate_chat( 408 | recipient=question_agent, 409 | message=( 410 | "Write Python code to print the current Python version followed by the numbers 1 to 11. " 411 | "Make a syntax error in the first version and fix it in the second version." 412 | ), 413 | max_turns=5, 414 | ) 415 | 416 | print(f"Result: {result.summary}") 417 | ``` 418 | 419 | #### Kaggle Dataset Visualization With LLM-Generated Code Using Ollama And Judge0 420 | 421 | ```python 422 | # pip install judge0 ollama requests 423 | import os 424 | import zipfile 425 | 426 | import judge0 427 | import requests 428 | from judge0 import File, Filesystem 429 | from ollama import Client 430 | 431 | # Step 1: Download the dataset from Kaggle. 432 | dataset_url = "https://www.kaggle.com/api/v1/datasets/download/gregorut/videogamesales" 433 | dataset_zip_path = "vgsales.zip" 434 | dataset_csv_path = "vgsales.csv" # P.S.: We know the CSV file name inside the zip. 435 | 436 | if not os.path.exists(dataset_csv_path): # Download only if not already downloaded. 437 | with requests.get(dataset_url) as response: 438 | with open(dataset_zip_path, "wb") as f: 439 | f.write(response.content) 440 | with zipfile.ZipFile(dataset_zip_path, "r") as f: 441 | f.extractall(".") 442 | 443 | # Step 2: Prepare the submission for Judge0. 444 | with open(dataset_csv_path, "r") as f: 445 | submission = judge0.Submission( 446 | language=judge0.PYTHON_FOR_ML, 447 | additional_files=Filesystem( 448 | content=[ 449 | File(name=dataset_csv_path, content=f.read()), 450 | ] 451 | ), 452 | ) 453 | 454 | # Step 3: Initialize Ollama Client. Get your free tier Ollama Cloud API key at https://ollama.com. 455 | client = Client( 456 | host="https://ollama.com", 457 | headers={"Authorization": "Bearer " + os.environ.get("OLLAMA_API_KEY")}, 458 | ) 459 | 460 | # Step 4: Prepare the prompt, messages, tools, and choose the model. 461 | prompt = f""" 462 | I have a CSV that contains a list of video games with sales greater than 100,000 copies. It's saved in the file {dataset_csv_path}. 463 | These are the columns: 464 | - 'Rank': Ranking of overall sales 465 | - 'Name': The games name 466 | - 'Platform': Platform of the games release (i.e. PC,PS4, etc.) 467 | - 'Year': Year of the game's release 468 | - 'Genre': Genre of the game 469 | - 'Publisher': Publisher of the game 470 | - 'NA_Sales': Sales in North America (in millions) 471 | - 'EU_Sales': Sales in Europe (in millions) 472 | - 'JP_Sales': Sales in Japan (in millions) 473 | - 'Other_Sales': Sales in the rest of the world (in millions) 474 | - 'Global_Sales': Total worldwide sales. 475 | 476 | I want to better understand how the sales are distributed across different genres over the years. 477 | Write Python code that analyzes the dataset based on my request, produces right chart and saves it as an image file. 478 | """ 479 | messages = [{"role": "user", "content": prompt}] 480 | tools = [ 481 | { 482 | "type": "function", 483 | "function": { 484 | "name": "execute_python", 485 | "description": "Execute the Python programming language code.", 486 | "parameters": { 487 | "type": "object", 488 | "properties": { 489 | "code": { 490 | "type": "string", 491 | "description": "The code written in the Python programming language.", 492 | } 493 | }, 494 | "required": ["code"], 495 | }, 496 | }, 497 | } 498 | ] 499 | model = "qwen3-coder:480b-cloud" 500 | 501 | # Step 5: Start the interaction with the model. 502 | response = client.chat(model=model, messages=messages, tools=tools) 503 | response_message = response["message"] 504 | 505 | if response_message.tool_calls: 506 | for tool_call in response_message.tool_calls: 507 | if tool_call.function.name == "execute_python": 508 | code = tool_call.function.arguments["code"] 509 | print(f"CODE GENERATED BY THE MODEL:\n{code}\n") 510 | 511 | submission.source_code = code 512 | result = judge0.run(submissions=submission) 513 | 514 | for f in result.post_execution_filesystem: 515 | if f.name.endswith((".png", ".jpg", ".jpeg")): 516 | with open(f.name, "wb") as img_file: 517 | img_file.write(f.content) 518 | print(f"Generated image saved as: {f.name}\n") 519 | ``` 520 | 521 | #### Minimal Example Using `smolagents` With Ollama And Judge0 522 | 523 | ```python 524 | # pip install judge0 smolagents[openai] 525 | import os 526 | from typing import Any 527 | 528 | import judge0 529 | from smolagents import CodeAgent, OpenAIServerModel, Tool 530 | from smolagents.local_python_executor import CodeOutput, PythonExecutor 531 | 532 | 533 | class Judge0PythonExecutor(PythonExecutor): 534 | def send_tools(self, tools: dict[str, Tool]) -> None: 535 | pass 536 | 537 | def send_variables(self, variables: dict[str, Any]) -> None: 538 | pass 539 | 540 | def __call__(self, code_action: str) -> CodeOutput: 541 | source_code = f"final_answer = lambda x : print(x)\n{code_action}" 542 | result = judge0.run(source_code=source_code, language=judge0.PYTHON_FOR_ML) 543 | return CodeOutput( 544 | output=result.stdout, 545 | logs=result.stderr or "", 546 | is_final_answer=result.exit_code == 0, 547 | ) 548 | 549 | 550 | # Get your free tier Ollama Cloud API key at https://ollama.com. 551 | model = OpenAIServerModel( 552 | model_id="gpt-oss:120b-cloud", 553 | api_base="https://ollama.com/v1", 554 | api_key=os.environ["OLLAMA_API_KEY"], 555 | ) 556 | 557 | agent = CodeAgent(tools=[], model=model) 558 | agent.python_executor = Judge0PythonExecutor() 559 | 560 | result = agent.run("How many r's are in the word 'strawberry'?") 561 | print(result) 562 | ``` 563 | 564 | ### Filesystem 565 | 566 | This example shows how to use Judge0 Python SDK to: 567 | 1. Create a submission with additional files in the filesystem which will be available during the execution. 568 | 2. Read the files after the execution which were created during the execution. 569 | 570 | ```python 571 | # pip install judge0 572 | import judge0 573 | from judge0 import Filesystem, File, Submission 574 | 575 | fs = Filesystem( 576 | content=[ 577 | File(name="./my_dir1/my_file1.txt", content="hello from my_file.txt"), 578 | ] 579 | ) 580 | 581 | source_code = """ 582 | cat ./my_dir1/my_file1.txt 583 | 584 | mkdir my_dir2 585 | echo "hello, world" > ./my_dir2/my_file2.txt 586 | """ 587 | 588 | submission = Submission( 589 | source_code=source_code, 590 | language=judge0.BASH, 591 | additional_files=fs, 592 | ) 593 | 594 | result = judge0.run(submissions=submission) 595 | 596 | print(result.stdout) 597 | print(result.post_execution_filesystem.find("./my_dir2/my_file2.txt")) 598 | ``` 599 | 600 | ### Custom Judge0 Client 601 | 602 | This example shows how to use Judge0 Python SDK with your own Judge0 instance. 603 | 604 | ```python 605 | # pip install judge0 606 | import judge0 607 | 608 | client = judge0.Client("http://127.0.0.1:2358") 609 | 610 | source_code = """ 611 | #include 612 | 613 | int main() { 614 | printf("hello, world\\n"); 615 | return 0; 616 | } 617 | """ 618 | 619 | result = judge0.run(client=client, source_code=source_code, language=judge0.C) 620 | print(result.stdout) 621 | ``` 622 | 623 | ### Generating And Saving An Image File 624 | 625 | ```python 626 | # pip install judge0 627 | import judge0 628 | 629 | source_code = """ 630 | import matplotlib.pyplot as plt 631 | 632 | plt.plot([x for x in range(10)], [x**2 for x in range(10)]) 633 | plt.savefig("chart.png") 634 | """ 635 | 636 | result = judge0.run(source_code=source_code, language=judge0.PYTHON_FOR_ML) 637 | 638 | image = result.post_execution_filesystem.find("chart.png") 639 | with open(image.name, "wb") as f: 640 | f.write(image.content) 641 | print(f"Generated image saved as: {image.name}\n") 642 | ``` 643 | -------------------------------------------------------------------------------- /src/judge0/clients.py: -------------------------------------------------------------------------------- 1 | from typing import ClassVar, Optional, Union 2 | 3 | import requests 4 | 5 | from .base_types import Config, Iterable, Language, LanguageAlias 6 | from .data import LANGUAGE_TO_LANGUAGE_ID 7 | from .retry import RetryStrategy 8 | from .submission import Submission, Submissions 9 | from .utils import handle_too_many_requests_error_for_preview_client 10 | 11 | 12 | class Client: 13 | """Base class for all clients. 14 | 15 | Parameters 16 | ---------- 17 | endpoint : str 18 | Client's default endpoint. 19 | auth_headers : dict 20 | Request authentication headers. 21 | 22 | Attributes 23 | ---------- 24 | API_KEY_ENV : str 25 | Environment variable where judge0-python should look for API key for 26 | the client. Set to default values for RapidAPI and ATD clients. 27 | """ 28 | 29 | # Environment variable where judge0-python should look for API key for 30 | # the client. Set to default values for RapidAPI and ATD clients. 31 | API_KEY_ENV: ClassVar[str] = None 32 | 33 | def __init__( 34 | self, 35 | endpoint, 36 | auth_headers=None, 37 | *, 38 | retry_strategy: Optional[RetryStrategy] = None, 39 | ) -> None: 40 | self.endpoint = endpoint 41 | self.auth_headers = auth_headers 42 | self.retry_strategy = retry_strategy 43 | self.session = requests.Session() 44 | 45 | try: 46 | self.languages = self.get_languages() 47 | self.config = self.get_config_info() 48 | except Exception as e: 49 | home_url = getattr(self, "HOME_URL", None) 50 | raise RuntimeError( 51 | f"Authentication failed. Visit {home_url} to get or " 52 | "review your authentication credentials." 53 | ) from e 54 | 55 | def __del__(self): 56 | self.session.close() 57 | 58 | @handle_too_many_requests_error_for_preview_client 59 | def get_about(self) -> dict: 60 | """Get general information about judge0. 61 | 62 | Returns 63 | ------- 64 | dict 65 | General information about judge0. 66 | """ 67 | response = self.session.get( 68 | f"{self.endpoint}/about", 69 | headers=self.auth_headers, 70 | ) 71 | response.raise_for_status() 72 | return response.json() 73 | 74 | @handle_too_many_requests_error_for_preview_client 75 | def get_config_info(self) -> Config: 76 | """Get information about client's configuration. 77 | 78 | Returns 79 | ------- 80 | Config 81 | Client's configuration. 82 | """ 83 | response = self.session.get( 84 | f"{self.endpoint}/config_info", 85 | headers=self.auth_headers, 86 | ) 87 | response.raise_for_status() 88 | return Config(**response.json()) 89 | 90 | @handle_too_many_requests_error_for_preview_client 91 | def get_language(self, language_id: int) -> Language: 92 | """Get language corresponding to the id. 93 | 94 | Parameters 95 | ---------- 96 | language_id : int 97 | Language id. 98 | 99 | Returns 100 | ------- 101 | Language 102 | Language corresponding to the passed id. 103 | """ 104 | request_url = f"{self.endpoint}/languages/{language_id}" 105 | response = self.session.get(request_url, headers=self.auth_headers) 106 | response.raise_for_status() 107 | return Language(**response.json()) 108 | 109 | @handle_too_many_requests_error_for_preview_client 110 | def get_languages(self) -> list[Language]: 111 | """Get a list of supported languages. 112 | 113 | Returns 114 | ------- 115 | list of language 116 | A list of supported languages. 117 | """ 118 | request_url = f"{self.endpoint}/languages" 119 | response = self.session.get(request_url, headers=self.auth_headers) 120 | response.raise_for_status() 121 | return [Language(**lang_dict) for lang_dict in response.json()] 122 | 123 | @handle_too_many_requests_error_for_preview_client 124 | def get_statuses(self) -> list[dict]: 125 | """Get a list of possible submission statuses. 126 | 127 | Returns 128 | ------- 129 | list of dict 130 | A list of possible submission statues. 131 | """ 132 | response = self.session.get( 133 | f"{self.endpoint}/statuses", 134 | headers=self.auth_headers, 135 | ) 136 | response.raise_for_status() 137 | return response.json() 138 | 139 | @property 140 | def version(self): 141 | """Property corresponding to the current client's version.""" 142 | if not hasattr(self, "_version"): 143 | _version = self.get_about()["version"] 144 | setattr(self, "_version", _version) 145 | return self._version 146 | 147 | def get_language_id(self, language: Union[LanguageAlias, int]) -> int: 148 | """Get language id corresponding to the language alias for the client. 149 | 150 | Parameters 151 | ---------- 152 | language : LanguageAlias or int 153 | Language alias or language id. 154 | 155 | Returns 156 | ------- 157 | Language id corresponding to the language alias. 158 | """ 159 | if isinstance(language, LanguageAlias): 160 | supported_language_ids = LANGUAGE_TO_LANGUAGE_ID[self.version] 161 | language = supported_language_ids.get(language, -1) 162 | return language 163 | 164 | def is_language_supported(self, language: Union[LanguageAlias, int]) -> bool: 165 | """Check if language is supported by the client. 166 | 167 | Parameters 168 | ---------- 169 | language : LanguageAlias or int 170 | Language alias or language id. 171 | 172 | Returns 173 | ------- 174 | bool 175 | Return True if language is supported by the client, otherwise returns 176 | False. 177 | """ 178 | language_id = self.get_language_id(language) 179 | return any(language_id == lang.id for lang in self.languages) 180 | 181 | @handle_too_many_requests_error_for_preview_client 182 | def create_submission(self, submission: Submission) -> Submission: 183 | """Send submission for execution to a client. 184 | 185 | Directly send a submission to create_submission route for execution. 186 | 187 | Parameters 188 | ---------- 189 | submission : Submission 190 | A submission to create. 191 | 192 | Returns 193 | ------- 194 | Submission 195 | A submission with updated token attribute. 196 | """ 197 | # Check if the client supports the language specified in the submission. 198 | if not self.is_language_supported(language=submission.language): 199 | raise RuntimeError( 200 | f"Client {type(self).__name__} does not support language with " 201 | f"id {submission.language}!" 202 | ) 203 | 204 | params = { 205 | "base64_encoded": "true", 206 | "wait": "false", 207 | } 208 | 209 | body = submission.as_body(self) 210 | 211 | response = self.session.post( 212 | f"{self.endpoint}/submissions", 213 | json=body, 214 | params=params, 215 | headers=self.auth_headers, 216 | ) 217 | response.raise_for_status() 218 | 219 | submission.set_attributes(response.json()) 220 | 221 | return submission 222 | 223 | @handle_too_many_requests_error_for_preview_client 224 | def get_submission( 225 | self, 226 | submission: Submission, 227 | *, 228 | fields: Optional[Union[str, Iterable[str]]] = None, 229 | ) -> Submission: 230 | """Get submissions status. 231 | 232 | Directly send submission's token to get_submission route for status 233 | check. By default, all submissions attributes (fields) are requested. 234 | 235 | Parameters 236 | ---------- 237 | submission : Submission 238 | Submission to update. 239 | 240 | Returns 241 | ------- 242 | Submission 243 | A Submission with updated attributes. 244 | """ 245 | params = { 246 | "base64_encoded": "true", 247 | } 248 | 249 | if isinstance(fields, str): 250 | fields = [fields] 251 | 252 | if fields is not None: 253 | params["fields"] = ",".join(fields) 254 | else: 255 | params["fields"] = "*" 256 | 257 | response = self.session.get( 258 | f"{self.endpoint}/submissions/{submission.token}", 259 | params=params, 260 | headers=self.auth_headers, 261 | ) 262 | response.raise_for_status() 263 | 264 | submission.set_attributes(response.json()) 265 | 266 | return submission 267 | 268 | @handle_too_many_requests_error_for_preview_client 269 | def create_submissions(self, submissions: Submissions) -> Submissions: 270 | """Send submissions for execution to a client. 271 | 272 | Directly send submissions to create_submissions route for execution. 273 | Cannot handle more submissions than the client supports. 274 | 275 | Parameters 276 | ---------- 277 | submissions : Submissions 278 | A sequence of submissions to create. 279 | 280 | Returns 281 | ------- 282 | Submissions 283 | A sequence of submissions with updated token attribute. 284 | """ 285 | for submission in submissions: 286 | if not self.is_language_supported(language=submission.language): 287 | raise RuntimeError( 288 | f"Client {type(self).__name__} does not support language " 289 | f"{submission.language}!" 290 | ) 291 | 292 | submissions_body = [submission.as_body(self) for submission in submissions] 293 | 294 | response = self.session.post( 295 | f"{self.endpoint}/submissions/batch", 296 | headers=self.auth_headers, 297 | params={"base64_encoded": "true"}, 298 | json={"submissions": submissions_body}, 299 | ) 300 | response.raise_for_status() 301 | 302 | for submission, attrs in zip(submissions, response.json()): 303 | submission.set_attributes(attrs) 304 | 305 | return submissions 306 | 307 | @handle_too_many_requests_error_for_preview_client 308 | def get_submissions( 309 | self, 310 | submissions: Submissions, 311 | *, 312 | fields: Optional[Union[str, Iterable[str]]] = None, 313 | ) -> Submissions: 314 | """Get submissions status. 315 | 316 | Directly send submissions' tokens to get_submissions route for status 317 | check. By default, all submissions attributes (fields) are requested. 318 | Cannot handle more submissions than the client supports. 319 | 320 | Parameters 321 | ---------- 322 | submissions : Submissions 323 | Submissions to update. 324 | 325 | Returns 326 | ------- 327 | Submissions 328 | A sequence of submissions with updated attributes. 329 | """ 330 | params = { 331 | "base64_encoded": "true", 332 | } 333 | 334 | if isinstance(fields, str): 335 | fields = [fields] 336 | 337 | if fields is not None: 338 | params["fields"] = ",".join(fields) 339 | else: 340 | params["fields"] = "*" 341 | 342 | tokens = ",".join([submission.token for submission in submissions]) 343 | params["tokens"] = tokens 344 | 345 | response = self.session.get( 346 | f"{self.endpoint}/submissions/batch", 347 | params=params, 348 | headers=self.auth_headers, 349 | ) 350 | response.raise_for_status() 351 | 352 | for submission, attrs in zip(submissions, response.json()["submissions"]): 353 | submission.set_attributes(attrs) 354 | 355 | return submissions 356 | 357 | 358 | class ATD(Client): 359 | """Base class for all AllThingsDev clients. 360 | 361 | Parameters 362 | ---------- 363 | endpoint : str 364 | Default request endpoint. 365 | host_header_value : str 366 | Value for the x-apihub-host header. 367 | api_key : str 368 | AllThingsDev API key. 369 | **kwargs : dict 370 | Additional keyword arguments for the base Client. 371 | """ 372 | 373 | API_KEY_ENV: ClassVar[str] = "JUDGE0_ATD_API_KEY" 374 | 375 | def __init__(self, endpoint, host_header_value, api_key, **kwargs): 376 | self.api_key = api_key 377 | super().__init__( 378 | endpoint, 379 | { 380 | "x-apihub-host": host_header_value, 381 | "x-apihub-key": api_key, 382 | }, 383 | **kwargs, 384 | ) 385 | 386 | def _update_endpoint_header(self, header_value): 387 | self.auth_headers["x-apihub-endpoint"] = header_value 388 | 389 | 390 | class ATDJudge0CE(ATD): 391 | """AllThingsDev client for CE flavor. 392 | 393 | Parameters 394 | ---------- 395 | api_key : str 396 | AllThingsDev API key. 397 | **kwargs : dict 398 | Additional keyword arguments for the base Client. 399 | """ 400 | 401 | DEFAULT_ENDPOINT: ClassVar[str] = ( 402 | "https://judge0-ce.proxy-production.allthingsdev.co" 403 | ) 404 | DEFAULT_HOST: ClassVar[str] = "Judge0-CE.allthingsdev.co" 405 | HOME_URL: ClassVar[str] = ( 406 | "https://www.allthingsdev.co/apimarketplace/judge0-ce/66b683c8b7b7ad054eb6ff8f" 407 | ) 408 | 409 | DEFAULT_ABOUT_ENDPOINT: ClassVar[str] = "01fc1c98-ceee-4f49-8614-f2214703e25f" 410 | DEFAULT_CONFIG_INFO_ENDPOINT: ClassVar[str] = "b7aab45d-5eb0-4519-b092-89e5af4fc4f3" 411 | DEFAULT_LANGUAGE_ENDPOINT: ClassVar[str] = "a50ae6b1-23c1-40eb-b34c-88bc8cf2c764" 412 | DEFAULT_LANGUAGES_ENDPOINT: ClassVar[str] = "03824deb-bd18-4456-8849-69d78e1383cc" 413 | DEFAULT_STATUSES_ENDPOINT: ClassVar[str] = "c37b603f-6f99-4e31-a361-7154c734f19b" 414 | DEFAULT_CREATE_SUBMISSION_ENDPOINT: ClassVar[str] = ( 415 | "6e65686d-40b0-4bf7-a12f-1f6d033c4473" 416 | ) 417 | DEFAULT_GET_SUBMISSION_ENDPOINT: ClassVar[str] = ( 418 | "b7032b8b-86da-40b4-b9d3-b1f5e2b4ee1e" 419 | ) 420 | DEFAULT_CREATE_SUBMISSIONS_ENDPOINT: ClassVar[str] = ( 421 | "402b857c-1126-4450-bfd8-22e1f2cbff2f" 422 | ) 423 | DEFAULT_GET_SUBMISSIONS_ENDPOINT: ClassVar[str] = ( 424 | "e42f2a26-5b02-472a-80c9-61c4bdae32ec" 425 | ) 426 | 427 | def __init__(self, api_key, **kwargs): 428 | super().__init__( 429 | self.DEFAULT_ENDPOINT, 430 | self.DEFAULT_HOST, 431 | api_key, 432 | **kwargs, 433 | ) 434 | 435 | def get_about(self) -> dict: 436 | self._update_endpoint_header(self.DEFAULT_ABOUT_ENDPOINT) 437 | return super().get_about() 438 | 439 | def get_config_info(self) -> Config: 440 | self._update_endpoint_header(self.DEFAULT_CONFIG_INFO_ENDPOINT) 441 | return super().get_config_info() 442 | 443 | def get_language(self, language_id) -> Language: 444 | self._update_endpoint_header(self.DEFAULT_LANGUAGE_ENDPOINT) 445 | return super().get_language(language_id) 446 | 447 | def get_languages(self) -> list[Language]: 448 | self._update_endpoint_header(self.DEFAULT_LANGUAGES_ENDPOINT) 449 | return super().get_languages() 450 | 451 | def get_statuses(self) -> list[dict]: 452 | self._update_endpoint_header(self.DEFAULT_STATUSES_ENDPOINT) 453 | return super().get_statuses() 454 | 455 | def create_submission(self, submission: Submission) -> Submission: 456 | self._update_endpoint_header(self.DEFAULT_CREATE_SUBMISSION_ENDPOINT) 457 | return super().create_submission(submission) 458 | 459 | def get_submission( 460 | self, 461 | submission: Submission, 462 | *, 463 | fields: Optional[Union[str, Iterable[str]]] = None, 464 | ) -> Submission: 465 | self._update_endpoint_header(self.DEFAULT_GET_SUBMISSION_ENDPOINT) 466 | return super().get_submission(submission, fields=fields) 467 | 468 | def create_submissions(self, submissions: Submissions) -> Submissions: 469 | self._update_endpoint_header(self.DEFAULT_CREATE_SUBMISSIONS_ENDPOINT) 470 | return super().create_submissions(submissions) 471 | 472 | def get_submissions( 473 | self, 474 | submissions: Submissions, 475 | *, 476 | fields: Optional[Union[str, Iterable[str]]] = None, 477 | ) -> Submissions: 478 | self._update_endpoint_header(self.DEFAULT_GET_SUBMISSIONS_ENDPOINT) 479 | return super().get_submissions(submissions, fields=fields) 480 | 481 | 482 | class ATDJudge0ExtraCE(ATD): 483 | """AllThingsDev client for Extra CE flavor. 484 | 485 | Parameters 486 | ---------- 487 | api_key : str 488 | AllThingsDev API key. 489 | **kwargs : dict 490 | Additional keyword arguments for the base Client. 491 | """ 492 | 493 | DEFAULT_ENDPOINT: ClassVar[str] = ( 494 | "https://judge0-extra-ce.proxy-production.allthingsdev.co" 495 | ) 496 | DEFAULT_HOST: ClassVar[str] = "Judge0-Extra-CE.allthingsdev.co" 497 | HOME_URL: ClassVar[str] = ( 498 | "https://www.allthingsdev.co/apimarketplace/judge0-extra-ce/" 499 | "66b68838b7b7ad054eb70690" 500 | ) 501 | 502 | DEFAULT_ABOUT_ENDPOINT: ClassVar[str] = "1fd631a1-be6a-47d6-bf4c-987e357e3096" 503 | DEFAULT_CONFIG_INFO_ENDPOINT: ClassVar[str] = "46e05354-2a43-436a-9458-5d111456f0ff" 504 | DEFAULT_LANGUAGE_ENDPOINT: ClassVar[str] = "10465a84-2a2c-4213-845f-45e3c04a5867" 505 | DEFAULT_LANGUAGES_ENDPOINT: ClassVar[str] = "774ecece-1200-41f7-a992-38f186c90803" 506 | DEFAULT_STATUSES_ENDPOINT: ClassVar[str] = "a2843b3c-673d-4966-9a14-2e7d76dcd0cb" 507 | DEFAULT_CREATE_SUBMISSION_ENDPOINT: ClassVar[str] = ( 508 | "be2d195e-dd58-4770-9f3c-d6c0fbc2b6e5" 509 | ) 510 | DEFAULT_GET_SUBMISSION_ENDPOINT: ClassVar[str] = ( 511 | "c3a457cd-37a6-4106-97a8-9e60a223abbc" 512 | ) 513 | DEFAULT_CREATE_SUBMISSIONS_ENDPOINT: ClassVar[str] = ( 514 | "c64df5d3-edfd-4b08-8687-561af2f80d2f" 515 | ) 516 | DEFAULT_GET_SUBMISSIONS_ENDPOINT: ClassVar[str] = ( 517 | "5d173718-8e6a-4cf5-9d8c-db5e6386d037" 518 | ) 519 | 520 | def __init__(self, api_key, **kwargs): 521 | super().__init__( 522 | self.DEFAULT_ENDPOINT, 523 | self.DEFAULT_HOST, 524 | api_key, 525 | **kwargs, 526 | ) 527 | 528 | def get_about(self) -> dict: 529 | self._update_endpoint_header(self.DEFAULT_ABOUT_ENDPOINT) 530 | return super().get_about() 531 | 532 | def get_config_info(self) -> Config: 533 | self._update_endpoint_header(self.DEFAULT_CONFIG_INFO_ENDPOINT) 534 | return super().get_config_info() 535 | 536 | def get_language(self, language_id) -> Language: 537 | self._update_endpoint_header(self.DEFAULT_LANGUAGE_ENDPOINT) 538 | return super().get_language(language_id) 539 | 540 | def get_languages(self) -> list[Language]: 541 | self._update_endpoint_header(self.DEFAULT_LANGUAGES_ENDPOINT) 542 | return super().get_languages() 543 | 544 | def get_statuses(self) -> list[dict]: 545 | self._update_endpoint_header(self.DEFAULT_STATUSES_ENDPOINT) 546 | return super().get_statuses() 547 | 548 | def create_submission(self, submission: Submission) -> Submission: 549 | self._update_endpoint_header(self.DEFAULT_CREATE_SUBMISSION_ENDPOINT) 550 | return super().create_submission(submission) 551 | 552 | def get_submission( 553 | self, 554 | submission: Submission, 555 | *, 556 | fields: Optional[Union[str, Iterable[str]]] = None, 557 | ) -> Submission: 558 | self._update_endpoint_header(self.DEFAULT_GET_SUBMISSION_ENDPOINT) 559 | return super().get_submission(submission, fields=fields) 560 | 561 | def create_submissions(self, submissions: Submissions) -> Submissions: 562 | self._update_endpoint_header(self.DEFAULT_CREATE_SUBMISSIONS_ENDPOINT) 563 | return super().create_submissions(submissions) 564 | 565 | def get_submissions( 566 | self, 567 | submissions: Submissions, 568 | *, 569 | fields: Optional[Union[str, Iterable[str]]] = None, 570 | ) -> Submissions: 571 | self._update_endpoint_header(self.DEFAULT_GET_SUBMISSIONS_ENDPOINT) 572 | return super().get_submissions(submissions, fields=fields) 573 | 574 | 575 | class Rapid(Client): 576 | """Base class for all RapidAPI clients. 577 | 578 | Parameters 579 | ---------- 580 | endpoint : str 581 | Default request endpoint. 582 | host_header_value : str 583 | Value for the x-rapidapi-host header. 584 | api_key : str 585 | RapidAPI API key. 586 | **kwargs : dict 587 | Additional keyword arguments for the base Client. 588 | """ 589 | 590 | API_KEY_ENV: ClassVar[str] = "JUDGE0_RAPID_API_KEY" 591 | 592 | def __init__(self, endpoint, host_header_value, api_key, **kwargs): 593 | self.api_key = api_key 594 | super().__init__( 595 | endpoint, 596 | { 597 | "x-rapidapi-host": host_header_value, 598 | "x-rapidapi-key": api_key, 599 | }, 600 | **kwargs, 601 | ) 602 | 603 | 604 | class RapidJudge0CE(Rapid): 605 | """RapidAPI client for CE flavor. 606 | 607 | Parameters 608 | ---------- 609 | api_key : str 610 | RapidAPI API key. 611 | **kwargs : dict 612 | Additional keyword arguments for the base Client. 613 | """ 614 | 615 | DEFAULT_ENDPOINT: ClassVar[str] = "https://judge0-ce.p.rapidapi.com" 616 | DEFAULT_HOST: ClassVar[str] = "judge0-ce.p.rapidapi.com" 617 | HOME_URL: ClassVar[str] = "https://rapidapi.com/judge0-official/api/judge0-ce" 618 | 619 | def __init__(self, api_key, **kwargs): 620 | super().__init__( 621 | self.DEFAULT_ENDPOINT, 622 | self.DEFAULT_HOST, 623 | api_key, 624 | **kwargs, 625 | ) 626 | 627 | 628 | class RapidJudge0ExtraCE(Rapid): 629 | """RapidAPI client for Extra CE flavor. 630 | 631 | Parameters 632 | ---------- 633 | api_key : str 634 | RapidAPI API key. 635 | **kwargs : dict 636 | Additional keyword arguments for the base Client. 637 | """ 638 | 639 | DEFAULT_ENDPOINT: ClassVar[str] = "https://judge0-extra-ce.p.rapidapi.com" 640 | DEFAULT_HOST: ClassVar[str] = "judge0-extra-ce.p.rapidapi.com" 641 | HOME_URL: ClassVar[str] = "https://rapidapi.com/judge0-official/api/judge0-extra-ce" 642 | 643 | def __init__(self, api_key, **kwargs): 644 | super().__init__( 645 | self.DEFAULT_ENDPOINT, 646 | self.DEFAULT_HOST, 647 | api_key, 648 | **kwargs, 649 | ) 650 | 651 | 652 | class Judge0Cloud(Client): 653 | """Base class for all Judge0 Cloud clients. 654 | 655 | Parameters 656 | ---------- 657 | endpoint : str 658 | Default request endpoint. 659 | auth_headers : str or dict 660 | Judge0 Cloud authentication headers, either as a JSON string or a dictionary. 661 | **kwargs : dict 662 | Additional keyword arguments for the base Client. 663 | """ 664 | 665 | def __init__(self, endpoint, auth_headers=None, **kwargs): 666 | if isinstance(auth_headers, str): 667 | from json import loads 668 | 669 | auth_headers = loads(auth_headers) 670 | 671 | super().__init__( 672 | endpoint, 673 | auth_headers, 674 | **kwargs, 675 | ) 676 | 677 | 678 | class Judge0CloudCE(Judge0Cloud): 679 | """Judge0 Cloud client for CE flavor. 680 | 681 | Parameters 682 | ---------- 683 | endpoint : str 684 | Default request endpoint. 685 | auth_headers : str or dict 686 | Judge0 Cloud authentication headers, either as a JSON string or a dictionary. 687 | **kwargs : dict 688 | Additional keyword arguments for the base Client. 689 | """ 690 | 691 | DEFAULT_ENDPOINT: ClassVar[str] = "https://ce.judge0.com" 692 | HOME_URL: ClassVar[str] = "https://ce.judge0.com" 693 | API_KEY_ENV: ClassVar[str] = "JUDGE0_CLOUD_CE_AUTH_HEADERS" 694 | 695 | def __init__(self, auth_headers=None, **kwargs): 696 | super().__init__( 697 | self.DEFAULT_ENDPOINT, 698 | auth_headers, 699 | **kwargs, 700 | ) 701 | 702 | 703 | class Judge0CloudExtraCE(Judge0Cloud): 704 | """Judge0 Cloud client for Extra CE flavor. 705 | 706 | Parameters 707 | ---------- 708 | endpoint : str 709 | Default request endpoint. 710 | auth_headers : str or dict 711 | Judge0 Cloud authentication headers, either as a JSON string or a dictionary. 712 | **kwargs : dict 713 | Additional keyword arguments for the base Client. 714 | """ 715 | 716 | DEFAULT_ENDPOINT: ClassVar[str] = "https://extra-ce.judge0.com" 717 | HOME_URL: ClassVar[str] = "https://extra-ce.judge0.com" 718 | API_KEY_ENV: ClassVar[str] = "JUDGE0_CLOUD_EXTRA_CE_AUTH_HEADERS" 719 | 720 | def __init__(self, auth_headers=None, **kwargs): 721 | super().__init__(self.DEFAULT_ENDPOINT, auth_headers, **kwargs) 722 | 723 | 724 | CE = (Judge0CloudCE, RapidJudge0CE, ATDJudge0CE) 725 | EXTRA_CE = (Judge0CloudExtraCE, RapidJudge0ExtraCE, ATDJudge0ExtraCE) 726 | --------------------------------------------------------------------------------