├── .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 |
7 | {%- for item in sorted_versions %}
8 | - {{ item.name }}
9 | {%- endfor %}
10 |
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 | 
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 | ''
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 |
--------------------------------------------------------------------------------