├── .gitignore ├── .travis.yml ├── LICENSE ├── README.rst ├── docs ├── Makefile └── source │ ├── client.rst │ ├── conf.py │ ├── index.rst │ ├── installation.rst │ └── submission.rst ├── judge0api ├── __init__.py ├── client.py ├── language.py ├── status.py └── submission.py ├── setup.py └── tests └── test_sample.py /.gitignore: -------------------------------------------------------------------------------- 1 | *.py[cod] 2 | 3 | # C extensions 4 | *.so 5 | 6 | # Packages 7 | *.egg 8 | *.egg-info 9 | dist 10 | build 11 | eggs 12 | parts 13 | bin 14 | var 15 | sdist 16 | develop-eggs 17 | .installed.cfg 18 | lib 19 | lib64 20 | __pycache__ 21 | 22 | # Installer logs 23 | pip-log.txt 24 | 25 | # Unit test / coverage reports 26 | .coverage 27 | .tox 28 | nosetests.xml 29 | 30 | # Translations 31 | *.mo 32 | 33 | # Mr Developer 34 | .mr.developer.cfg 35 | .project 36 | .pydevproject 37 | 38 | \.idea/ 39 | 40 | \.DS_Store 41 | 42 | coverage\.xml 43 | 44 | cc-test-reporter 45 | 46 | \.eggs/ 47 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | dist: xenial 2 | language: python 3 | python: 3.7 4 | install: pip install setuptools 5 | before_script: 6 | - curl -L https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64 7 | > ./cc-test-reporter 8 | - chmod +x ./cc-test-reporter 9 | - "./cc-test-reporter before-build" 10 | - 11 | script: python setup.py pytest --addopts "--cov=judge0api --cov-report=xml" 12 | after_script: 13 | - "./cc-test-reporter after-build --id 082afe93df2754b0c060ed4a564d5ec7b08ae5e73a47ad7bfd771b503771262f 14 | --exit-code $TRAVIS_TEST_RESULT" 15 | deploy: 16 | provider: pypi 17 | user: vCra 18 | password: 19 | secure: hudzuXMwwE/CFXRzqlWlsQdk9Ig4vdHNF29kd+NwqIi0MO6UPaNytXBdi2cTXn7SxsvzGTmDKaXLHArqAVL3yoXg45NEk0WzOgYmScY2HeSUXAOEXIeB4BS/wn8CnnGsG6d4u22C0Z3S24aUwqkj8H2yBnntXQ0TvfNXv8ODLdMMJw757KvEpNfvWGZkolFmzzXgZGs/fnGeM6uYDYpNxZd4jTDgfDjCXLkelwsqY/ttRPPPsTlyPTwnVdTmJ1oIT7F3jOQ6MpSmwu53AcV2e3nQg5wQmWPhrXpMhTREGLmDxv2Fm4/RSvvHj8OJ/6dccLkqVgKo4nKZEMFLwF94G0r7OjylwORMrxAM3S5nWi4DyYp8iOEr/f1/3CrvyFOdq6WWYtkw5RAX5aBnJAaG7YsOPTbwSvDuWSqmSxVsLVgOxAuTjTMmwK+joQqOz6LclvmUMzi/o7ui9fyiZFMlHaz7d4jPqxby7S/WSba9Uy90uGTQFhc6UuyF0glRQ+l+FU8qYcL9HczhuluNQEpJ9oFlvdZ54nuG0hYVnUgjngN6a2ZV2gYdinVx2XjXi0GYKjkEQWpgYvFzULiQFONl4WxWhDGFQ7nfEtLY+a/TQb+Bj9J+Cw5rXgBgJHYuAimkWNRbL8Idq5Ds01w02txyvxaZgCb7hkTHIfPwhA48fD4= 20 | on: 21 | branch: master 22 | distributions: sdist 23 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2019 Aaron Walker 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. -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | judge0api 2 | =========== 3 | 4 | .. image:: https://img.shields.io/pypi/v/judge0api.svg 5 | :target: https://pypi.python.org/pypi/judge0api 6 | :alt: Latest PyPI version 7 | 8 | .. image:: https://img.shields.io/pypi/dm/judge0api.svg 9 | :target: https://pypi.python.org/pypi/judge0api/ 10 | 11 | .. image:: https://img.shields.io/pypi/pyversions/judge0api.svg 12 | :target: https://pypi.python.org/pypi/judge0api/ 13 | 14 | .. image:: https://travis-ci.com/vCra/judge0api.svg?branch=master 15 | :target: https://travis-ci.com/vCra/judge0api 16 | 17 | .. image:: https://readthedocs.org/projects/judge0api/badge/?version=latest 18 | :target: http://judge0api.readthedocs.io/?badge=latest 19 | 20 | .. image:: https://api.codeclimate.com/v1/badges/f61d15c277295dbbfa46/maintainability 21 | :target: https://codeclimate.com/github/vCra/judge0api/maintainability 22 | :alt: Maintainability 23 | 24 | .. image:: https://api.codeclimate.com/v1/badges/f61d15c277295dbbfa46/test_coverage 25 | :target: https://codeclimate.com/github/vCra/judge0api/test_coverage 26 | :alt: Test Coverage 27 | 28 | .. image:: https://img.shields.io/github/license/vCra/judge0api.svg 29 | :target: https://github.com/vCra/judge0api/blob/master/LICENSE 30 | 31 | 32 | judge0api is a Python API for interacting with the Judge0 REST API. 33 | 34 | Usage 35 | ----- 36 | Using this tool is super easy! 37 | 38 | >>> import judge0api as api 39 | >>> client = api.Client("https://api.judge0.com") 40 | >>> client.wait = False # Not needed on servers which support wait 41 | >>> submission = api.submission.submit( 42 | ... client, 43 | ... b"print(f'Hello {input()}')", 44 | ... 71, 45 | ... stdin=b'Judge0', 46 | ... expected_output=b"Hello Judge0" 47 | ... ) 48 | >>> submission.load(client) # Load the status from the server 49 | >>> submission.stdout 50 | 'Hello Judge0' 51 | 52 | Installation 53 | ------------ 54 | 55 | Simply run ``pip install judge0api`` 56 | 57 | Requirements 58 | ^^^^^^^^^^^^ 59 | 60 | `Python requests 61 | `_ is required. 62 | 63 | Compatibility 64 | ------------- 65 | 66 | Only Python >= 3.6 is supported. 67 | 68 | Help! 69 | ----- 70 | 71 | Your first place to get help should be `The Docs 72 | `_! 73 | 74 | However: 75 | Is something not working properly? Are the docs awful? Want to help make this better? 76 | If the answer is yes then great! All you have to do is open an issue. 77 | 78 | Licence 79 | ------- 80 | 81 | This software is released under the MIT License 82 | 83 | Authors 84 | ------- 85 | 86 | `judge0api` was written by `Aaron Walker `_. 87 | -------------------------------------------------------------------------------- /docs/Makefile: -------------------------------------------------------------------------------- 1 | # Minimal makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line. 5 | SPHINXOPTS = 6 | SPHINXBUILD = sphinx-build 7 | SOURCEDIR = source 8 | BUILDDIR = build 9 | 10 | # Put it first so that "make" without argument is like "make help". 11 | help: 12 | @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 13 | 14 | .PHONY: help Makefile 15 | 16 | # Catch-all target: route all unknown targets to Sphinx using the new 17 | # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). 18 | %: Makefile 19 | @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) -------------------------------------------------------------------------------- /docs/source/client.rst: -------------------------------------------------------------------------------- 1 | Client 2 | ====== 3 | 4 | .. toctree:: 5 | :maxdepth: 2 6 | :caption: Contents: 7 | 8 | judge0api uses the concept of a "client" in order to interface with a Judge0 API. The client holds information about 9 | the following: 10 | 11 | - The "Endpoint" server - this is normally a domain, with a scheme. The public endpoint is https://api.judge0.com/, although different schemes, and even ports can be used. 12 | - Any authentication information that is needed 13 | - Any authorization information that is needed 14 | 15 | .. autoclass:: judge0api.Client -------------------------------------------------------------------------------- /docs/source/conf.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # Configuration file for the Sphinx documentation builder. 4 | # 5 | # This file does only contain a selection of the most common options. For a 6 | # full list see the documentation: 7 | # http://www.sphinx-doc.org/en/master/config 8 | 9 | # -- Path setup -------------------------------------------------------------- 10 | 11 | # If extensions (or modules to document with autodoc) are in another directory, 12 | # add these directories to sys.path here. If the directory is relative to the 13 | # documentation root, use os.path.abspath to make it absolute, like shown here. 14 | # 15 | # import os 16 | # import sys 17 | # sys.path.insert(0, os.path.abspath('.')) 18 | 19 | 20 | # -- Project information ----------------------------------------------------- 21 | 22 | project = 'judge0api' 23 | copyright = '2019, Aaron Walker ' 24 | author = 'Aaron Walker ' 25 | 26 | # The short X.Y version 27 | version = '' 28 | # The full version, including alpha/beta/rc tags 29 | release = '0.1.19' 30 | 31 | 32 | # -- General configuration --------------------------------------------------- 33 | 34 | # If your documentation needs a minimal Sphinx version, state it here. 35 | # 36 | # needs_sphinx = '1.0' 37 | 38 | # Add any Sphinx extension module names here, as strings. They can be 39 | # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom 40 | # ones. 41 | extensions = [ 42 | 'sphinx.ext.autodoc', 43 | 'sphinx.ext.doctest', 44 | 'sphinx.ext.intersphinx', 45 | 'sphinx.ext.todo', 46 | 'sphinx.ext.coverage', 47 | 'sphinx.ext.viewcode', 48 | ] 49 | 50 | # Add any paths that contain templates here, relative to this directory. 51 | templates_path = ['_templates'] 52 | 53 | # The suffix(es) of source filenames. 54 | # You can specify multiple suffix as a list of string: 55 | # 56 | # source_suffix = ['.rst', '.md'] 57 | source_suffix = '.rst' 58 | 59 | # The master toctree document. 60 | master_doc = 'index' 61 | 62 | # The language for content autogenerated by Sphinx. Refer to documentation 63 | # for a list of supported languages. 64 | # 65 | # This is also used if you do content translation via gettext catalogs. 66 | # Usually you set "language" from the command line for these cases. 67 | language = None 68 | 69 | # List of patterns, relative to source directory, that match files and 70 | # directories to ignore when looking for source files. 71 | # This pattern also affects html_static_path and html_extra_path. 72 | exclude_patterns = [] 73 | 74 | # The name of the Pygments (syntax highlighting) style to use. 75 | pygments_style = None 76 | 77 | 78 | # -- Options for HTML output ------------------------------------------------- 79 | 80 | # The theme to use for HTML and HTML Help pages. See the documentation for 81 | # a list of builtin themes. 82 | # 83 | html_theme = 'alabaster' 84 | 85 | # Theme options are theme-specific and customize the look and feel of a theme 86 | # further. For a list of options available for each theme, see the 87 | # documentation. 88 | # 89 | # html_theme_options = {} 90 | 91 | # Add any paths that contain custom static files (such as style sheets) here, 92 | # relative to this directory. They are copied after the builtin static files, 93 | # so a file named "default.css" will overwrite the builtin "default.css". 94 | html_static_path = ['_static'] 95 | 96 | # Custom sidebar templates, must be a dictionary that maps document names 97 | # to template names. 98 | # 99 | # The default sidebars (for documents that don't match any pattern) are 100 | # defined by theme itself. Builtin themes are using these templates by 101 | # default: ``['localtoc.html', 'relations.html', 'sourcelink.html', 102 | # 'searchbox.html']``. 103 | # 104 | # html_sidebars = {} 105 | 106 | 107 | # -- Options for HTMLHelp output --------------------------------------------- 108 | 109 | # Output file base name for HTML help builder. 110 | htmlhelp_basename = 'judge0apidoc' 111 | 112 | 113 | # -- Options for LaTeX output ------------------------------------------------ 114 | 115 | latex_elements = { 116 | # The paper size ('letterpaper' or 'a4paper'). 117 | # 118 | # 'papersize': 'letterpaper', 119 | 120 | # The font size ('10pt', '11pt' or '12pt'). 121 | # 122 | # 'pointsize': '10pt', 123 | 124 | # Additional stuff for the LaTeX preamble. 125 | # 126 | # 'preamble': '', 127 | 128 | # Latex figure (float) alignment 129 | # 130 | # 'figure_align': 'htbp', 131 | } 132 | 133 | # Grouping the document tree into LaTeX files. List of tuples 134 | # (source start file, target name, title, 135 | # author, documentclass [howto, manual, or own class]). 136 | latex_documents = [ 137 | (master_doc, 'judge0api.tex', 'judge0api Documentation', 138 | 'Aaron Walker \\textless{}aaron at vcra dot io\\textgreater{}', 'manual'), 139 | ] 140 | 141 | 142 | # -- Options for manual page output ------------------------------------------ 143 | 144 | # One entry per manual page. List of tuples 145 | # (source start file, name, description, authors, manual section). 146 | man_pages = [ 147 | (master_doc, 'judge0api', 'judge0api Documentation', 148 | [author], 1) 149 | ] 150 | 151 | 152 | # -- Options for Texinfo output ---------------------------------------------- 153 | 154 | # Grouping the document tree into Texinfo files. List of tuples 155 | # (source start file, target name, title, author, 156 | # dir menu entry, description, category) 157 | texinfo_documents = [ 158 | (master_doc, 'judge0api', 'judge0api Documentation', 159 | author, 'judge0api', 'A Python API for Judge0', 160 | 'Miscellaneous'), 161 | ] 162 | 163 | 164 | # -- Options for Epub output ------------------------------------------------- 165 | 166 | # Bibliographic Dublin Core info. 167 | epub_title = project 168 | 169 | # The unique identifier of the text. This can be a ISBN number 170 | # or the project homepage. 171 | # 172 | # epub_identifier = '' 173 | 174 | # A unique identification for the text. 175 | # 176 | # epub_uid = '' 177 | 178 | # A list of files that should not be packed into the epub file. 179 | epub_exclude_files = ['search.html'] 180 | 181 | 182 | # -- Extension configuration ------------------------------------------------- 183 | 184 | # -- Options for intersphinx extension --------------------------------------- 185 | 186 | # Example configuration for intersphinx: refer to the Python standard library. 187 | intersphinx_mapping = {'https://docs.python.org/': None} 188 | 189 | # -- Options for todo extension ---------------------------------------------- 190 | 191 | # If true, `todo` and `todoList` produce output, else they produce nothing. 192 | todo_include_todos = True 193 | -------------------------------------------------------------------------------- /docs/source/index.rst: -------------------------------------------------------------------------------- 1 | .. judge0api documentation master file, created by 2 | sphinx-quickstart on Sun Apr 28 19:06:59 2019. 3 | You can adapt this file completely to your liking, but it should at least 4 | contain the root `toctree` directive. 5 | 6 | Welcome to judge0api's documentation! 7 | ===================================== 8 | 9 | judge0api is a simple API for use with Judge0 API. It acts as a nicer way of interfacing with Judge0, without the 10 | need to remember REST API's. It currently supports the following: 11 | 12 | - Submitting a contest 13 | - Getting a contest 14 | 15 | .. toctree:: 16 | :maxdepth: 2 17 | :caption: Contents: 18 | 19 | installation.rst 20 | client.rst 21 | submission.rst 22 | 23 | Indices and tables 24 | ================== 25 | 26 | * :ref:`genindex` 27 | * :ref:`modindex` 28 | * :ref:`search` 29 | 30 | -------------------------------------------------------------------------------- /docs/source/installation.rst: -------------------------------------------------------------------------------- 1 | Installation 2 | ============ 3 | 4 | .. toctree:: 5 | :maxdepth: 2 6 | :caption: Contents: 7 | 8 | In most cases, you will want to install judge0api from pip. To do this, simply run: 9 | 10 | pip install api 11 | 12 | Requirements 13 | ------------ 14 | 15 | The only dependency for judge0api is Python Requests. It will get installed during installation if not already 16 | present. 17 | 18 | Python 3.6 (or greater) is required. Lower versions will not work. 19 | 20 | 21 | -------------------------------------------------------------------------------- /docs/source/submission.rst: -------------------------------------------------------------------------------- 1 | Submission 2 | ============ 3 | 4 | .. toctree:: 5 | :maxdepth: 2 6 | :caption: Contents: 7 | 8 | .. automodule:: judge0api.submission 9 | :members: 10 | 11 | -------------------------------------------------------------------------------- /judge0api/__init__.py: -------------------------------------------------------------------------------- 1 | """judge0api - A Python API for interacting with the Judge0""" 2 | 3 | 4 | from . import language, submission 5 | from .client import Client 6 | from .status import Judge0Status 7 | 8 | __version__ = '0.1.19' 9 | __author__ = 'Aaron Walker ' 10 | __all__ = ['client', 'submission', 'language', 'Judge0Status'] 11 | 12 | if __name__ == '__main__': 13 | pass 14 | -------------------------------------------------------------------------------- /judge0api/client.py: -------------------------------------------------------------------------------- 1 | import requests 2 | 3 | 4 | class Client: 5 | """ 6 | A judge0api Client 7 | 8 | Stores Endpoint information, as well as auth tokens, as well as submission wait preferences 9 | 10 | The constructor handles most things - if you don't want this client to wait for submissions, then set wait to False 11 | """ 12 | auth_token = None 13 | auth_user = None 14 | session = None 15 | endpoint = None 16 | wait = True 17 | 18 | def __init__(self, endpoint, auth_token=None, auth_user=None): 19 | """ 20 | Creates an instance of a judge0api Client 21 | :param endpoint: The "Endpoint" server - this is normally a domain, with a scheme. e.g. https://api.judge0.com/ 22 | :param auth_token: An optional auth_token - this is normally not needed 23 | :param auth_user: An optional auth_user - this is normally not needed 24 | """ 25 | self.endpoint = endpoint 26 | self.auth_token = auth_token 27 | self.auth_user = auth_user 28 | self.session = requests.session() 29 | self._login() 30 | 31 | def _login(self): 32 | """ 33 | If this session has any auth tokens, ensure that they are valid. 34 | :return: 35 | """ 36 | if self.auth_user: 37 | self._authorize() 38 | if self.auth_token: 39 | self._authenticate() 40 | 41 | def _authenticate(self): 42 | """ 43 | Authenticate the auth_token against this clients endpoint 44 | :raises HTTPException if token is invalid 45 | """ 46 | header = {"X-Auth-Token": self.auth_token} 47 | r = requests.post(f"{self.endpoint}/authenticate", params=header) 48 | r.raise_for_status() 49 | self.session.headers.update(header) 50 | 51 | def _authorize(self): 52 | """ 53 | Authorize the auth_user against this clients endpoint 54 | :raises HTTPException if token is invalid 55 | """ 56 | header = {"X-Auth-User": self.auth_user} 57 | r = requests.post(f"{self.endpoint}/authorize", params=header) 58 | r.raise_for_status() 59 | self.session.headers.update(header) 60 | -------------------------------------------------------------------------------- /judge0api/language.py: -------------------------------------------------------------------------------- 1 | class Language: 2 | verbose_name = None 3 | language_id = None 4 | 5 | 6 | def load(client): 7 | headers = {"Content-Type": "application/json"} 8 | r = client.endpoint.get(f"{client.endpoint}/submissions/", headers=headers) 9 | r.raise_for_status() 10 | 11 | languages = [] 12 | 13 | for l in r.json(): 14 | language = Language() 15 | language.verbose_name = l["name"] 16 | language.language_id = l["id"] 17 | languages.append(language) 18 | 19 | return languages 20 | 21 | -------------------------------------------------------------------------------- /judge0api/status.py: -------------------------------------------------------------------------------- 1 | from enum import Enum 2 | 3 | 4 | class Judge0Status(Enum): 5 | QUEUED = 1 6 | PROCESSING = 2 7 | ACCEPTED = 3 8 | WRONG_ANSWER = 4 9 | TIMEOUT = 5 10 | COMPILATION_ERR = 6 11 | RUNTIME_SIGSEGV = 7 12 | RUNTIME_SIGXFSZ = 8 13 | RUNTIME_SIGFPE = 9 14 | RUNTIME_SIGABRT = 10 15 | RUNTIME_NZEC = 11 16 | RUNTIME_OTHER = 12 17 | INTERNAL_ERR = 13 18 | EXEC_FORMAT_ERR = 14 19 | -------------------------------------------------------------------------------- /judge0api/submission.py: -------------------------------------------------------------------------------- 1 | import base64 2 | 3 | 4 | class Submission: 5 | """ 6 | Stores a representation of a Submission to/from Judge0API 7 | 8 | This class has the following properties: 9 | 10 | ### Fields that will get sent to Judge0 11 | 12 | # The language ID 13 | language_id = None 14 | 15 | # These 3 must always be bytestrings. 16 | source_code 17 | stdin 18 | expected_output 19 | 20 | # Extra Send Fields 21 | cpu_time_limit = None 22 | cpu_extra_time = None 23 | wall_time_limit = None 24 | memory_limit = None 25 | stack_limit = None 26 | max_processes_and_or_threads = None 27 | enable_per_process_and_thread_time_limit = None 28 | enable_per_process_and_thread_memory_limit = None 29 | max_file_size = None 30 | number_of_runs = None 31 | 32 | ### Fields that will be retrieved from Judge0: 33 | 34 | # These 3 should always be bytestrings 35 | compile_output = None 36 | stdout = None 37 | stderr = None 38 | 39 | # Receive Fields 40 | message = None 41 | exit_code = None 42 | exit_signal = None 43 | status = None 44 | created_at = None 45 | finished_at = None 46 | token = None 47 | time = None 48 | wall_time = None 49 | memory = None 50 | 51 | In addition, the "objective" of a field can be found in the following properties - the name of which should 52 | be self explanatory: 53 | 54 | _encoded_send_fields 55 | _encoded_response_fields 56 | _encoded_fields 57 | _extra_send_fields 58 | _response_fields 59 | _extra_response_fields 60 | _send_fields 61 | _fields 62 | 63 | """ 64 | # These 3 should always be bytestrings interally 65 | source_code = None 66 | stdin = None 67 | expected_output = None 68 | 69 | # The language ID 70 | language_id = None 71 | 72 | # Extra Send Fields 73 | cpu_time_limit = None 74 | cpu_extra_time = None 75 | wall_time_limit = None 76 | memory_limit = None 77 | stack_limit = None 78 | max_processes_and_or_threads = None 79 | enable_per_process_and_thread_time_limit = None 80 | enable_per_process_and_thread_memory_limit = None 81 | max_file_size = None 82 | number_of_runs = None 83 | 84 | # These 3 should always be bytestrings interally 85 | compile_output = None 86 | stdout = None 87 | stderr = None 88 | 89 | # Receive Fields 90 | message = None 91 | exit_code = None 92 | exit_signal = None 93 | status = None 94 | created_at = None 95 | finished_at = None 96 | token = None 97 | time = None 98 | wall_time = None 99 | memory = None 100 | 101 | # Please excuse the mess - this simply helps getting the correct fields easier 102 | _encoded_send_fields = {"source_code", "stdin", "expected_output"} 103 | _encoded_response_fields = {"stderr", "stdout", "compile_output"} 104 | _encoded_fields = _encoded_send_fields | _encoded_response_fields 105 | _extra_send_fields = {"cpu_time_limit", "cpu_extra_time", "wall_time_limit", "memory_limit", "stack_limit", "max_processes_and_or_threads", "enable_per_process_and_thread_time_limit", "enable_per_process_and_thread_memory_limit", "max_file_size", "number_of_runs"} 106 | _extra_response_fields = {"time", "memory", "token", "message", "status", "exit_code", "exit_signal", "created_at", "finished_at", "wall_time"} 107 | _response_fields = _encoded_response_fields | _extra_response_fields | {"language_id"} 108 | _send_fields = _encoded_send_fields | _extra_send_fields 109 | _fields = _response_fields | _send_fields 110 | 111 | def keys(self): 112 | """ 113 | Returns a list of all fields used by Judge0 114 | :return: 115 | """ 116 | return list(self._fields) 117 | 118 | def __getitem__(self, item): 119 | """ 120 | Gets an Item by key - decodes the field if encoded 121 | :param item: 122 | :return: 123 | """ 124 | if item in self._encoded_fields: 125 | # If this does not work, then at some point the data has not been set as bytes internally 126 | item = getattr(self, item) 127 | if item: 128 | return item.decode() 129 | return None 130 | 131 | return getattr(self, item) 132 | 133 | def load(self, client): 134 | """ 135 | Populates the object if it has a token 136 | :param client: the Judge0API client 137 | """ 138 | headers = {"Content-Type": "application/json"} 139 | params = { 140 | "base64_encoded": "true", 141 | # The fields parameter of judge0 expects a comma separated string rather than a HTML spec param list 142 | "fields": ",".join(self._response_fields) 143 | } 144 | r = client.session.get(f"{client.endpoint}/submissions/{self.token}/", headers=headers, params=params) 145 | r.raise_for_status() 146 | 147 | json = r.json() 148 | self.set_properties(dict(json)) 149 | 150 | def submit(self, client): 151 | """ 152 | Submits this Submission. Requires that self.language_id, and self.source_code is not none 153 | In addition, self.stdin and self.expected output can be set, in order to validate the result on Judge0 154 | :param client: the Judge0API Client 155 | :raises HTTPError if the post request is not able to be completed 156 | """ 157 | headers = {"Content-Type": "application/json"} 158 | params = {"base64_encoded": "true", "wait": str(client.wait).lower()} 159 | language_id = self.language_id 160 | 161 | data = { 162 | "source_code": base64.b64encode(self.source_code).decode('ascii'), 163 | "language_id": language_id, 164 | } 165 | if self.stdin: 166 | data.update({"stdin": base64.b64encode(self.stdin).decode('ascii')}) 167 | if self.expected_output: 168 | data.update({"expected_output": base64.b64encode(self.expected_output).decode('ascii')}) 169 | 170 | for field in self._extra_send_fields: 171 | if self.__getattribute__(field) is not None: 172 | data.update({field: self.__getattribute__(field)}) 173 | 174 | r = client.session.post(f"{client.endpoint}/submissions/", headers=headers, params=params, json=data) 175 | r.raise_for_status() 176 | 177 | json = r.json() 178 | self.set_properties(dict(json)) 179 | 180 | def set_properties(self, r): 181 | """ 182 | Takes a dict, and sets each value of the dict into Submission, base64 encoding it if required 183 | :param r: 184 | :return: 185 | """ 186 | for key, value in r.items(): 187 | if key in self._encoded_fields: 188 | # TODO: make nicer 189 | # A key might be present, but with no Value 190 | setattr(self, key, base64.b64decode(value.encode()) if value else None) 191 | else: 192 | setattr(self, key, value) 193 | 194 | 195 | def get(client, submission_token): 196 | """ 197 | Retrieves a submission from its token 198 | :param client: Judge0API client 199 | :param submission_token: token, as a string 200 | :return: 201 | """ 202 | submission = Submission() 203 | submission.token = submission_token 204 | submission.load(client) 205 | return submission 206 | 207 | 208 | def submit(client, source_code, language, stdin=None, expected_output=None, **kwargs): 209 | """ 210 | Creates and submits 211 | :param client: the judge0 client object 212 | :param source_code: a byte-string of the source code 213 | :param language: the language ID for this program 214 | :param stdin: a byte-string of the input for this program 215 | :param expected_output: a byte-string of the expected output 216 | :param **kwargs: any other extra arguments to be sent to the judge 217 | :return: submission 218 | """ 219 | submission = Submission() 220 | submission.set_properties(kwargs) 221 | submission.source_code = source_code 222 | submission.language_id = language 223 | submission.stdin = stdin 224 | submission.expected_output = expected_output 225 | submission.submit(client) 226 | return submission 227 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | import io 2 | import os 3 | import re 4 | 5 | from setuptools import find_packages 6 | from setuptools import setup 7 | 8 | 9 | def read(filename): 10 | filename = os.path.join(os.path.dirname(__file__), filename) 11 | text_type = type(u"") 12 | with io.open(filename, mode="r", encoding='utf-8') as fd: 13 | return re.sub(text_type(r':[a-z]+:`~?(.*?)`'), text_type(r'``\1``'), fd.read()) 14 | 15 | 16 | setup( 17 | name="judge0api", 18 | version="0.1.20", 19 | url="https://github.com/vCra/judge0api", 20 | license='MIT', 21 | 22 | author="Aaron Walker", 23 | author_email="aaron@vcra.io", 24 | 25 | description="A Python API for interacting with the Judge0 REST API", 26 | long_description=read("README.rst"), 27 | 28 | packages=find_packages(exclude=('tests',)), 29 | 30 | install_requires=[ 31 | 'requests' 32 | ], 33 | setup_requires=[ 34 | 'pytest-runner', 35 | ], 36 | tests_require=[ 37 | 'pytest', 38 | 'pytest-cov' 39 | ], 40 | 41 | classifiers=[ 42 | 'Development Status :: 4 - Beta', 43 | 'License :: OSI Approved :: MIT License', 44 | 'Programming Language :: Python', 45 | 'Programming Language :: Python :: 3', 46 | 'Programming Language :: Python :: 3.6', 47 | 'Programming Language :: Python :: 3.7', 48 | 'Programming Language :: Python :: 3.8', 49 | ], 50 | ) 51 | -------------------------------------------------------------------------------- /tests/test_sample.py: -------------------------------------------------------------------------------- 1 | # Sample Test passing with nose and pytest 2 | 3 | def test_pass(): 4 | assert True, "dummy sample test" 5 | 6 | 7 | def test_homepage_example(): 8 | import judge0api as api 9 | import time 10 | 11 | client = api.Client("https://ce.judge0.com") 12 | client.wait = False 13 | 14 | submission = api.submission.submit( 15 | client, 16 | b"print(f'Hello {input()}')", 17 | 71, 18 | stdin=b'Judge0', 19 | expected_output=b"Hello Judge0" 20 | ) 21 | time.sleep(3) 22 | submission.load(client) 23 | 24 | assert submission.status['id'] == api.Judge0Status.ACCEPTED.value 25 | --------------------------------------------------------------------------------