├── .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 |
--------------------------------------------------------------------------------