# coding=utf-8 import ast import multiprocessing.managers import threading import server.user_session from db.models import Problem from . import problems __all__ = ['PythonSession'] class PythonSession(object): """Abstracts a Python session. Only public methods are available to the outside world due to the use of multiprocessing managers. Therefore prefix any private methods with an underscore (_). No properties are accessible; use getters and setters instead. Values are passed by value instead of by reference (deep copy!). """ def __init__(self): self._access_lock = threading.Lock() self._sent_hints = [] self._interpreter = None # Proxy for calling the Python runner. We use a separate connection for # each session so the runner can be restarted without affecting the # server. _m = multiprocessing.managers.BaseManager(address=('localhost', 3031), authkey=b'c0d3q3y-python') _m.register('Python') _m.connect() self._python = _m.Python() self.create() def run(self, code=None, inputs=None, timeout=1.0): with self._access_lock: return self._python.run(code, inputs, timeout) def create(self): with self._access_lock: if self._interpreter is None: self._interpreter = self._python.create() def pull(self): with self._access_lock: if self._interpreter is None: return None return self._python.pull(self._interpreter) def push(self, stdin): with self._access_lock: if self._interpreter is not None: self._python.push(self._interpreter, stdin) def destroy(self): with self._access_lock: if self._interpreter is not None: self._python.destroy(self._interpreter) self._interpreter = None def __del__(self): # no locking needed if GC is removing us, as there cannot be any concurrent access by definition if hasattr(self, '_interpreter') and self._interpreter is not None: self._python.destroy(self._interpreter) self._interpreter = None def hint(self, sid, problem_id, program): language, problem_group, problem = Problem.get_identifier(problem_id) language_module = problems.load_language(language, 'common') problem_module = problems.load_problem(language, problem_group, problem, 'common') hints = [] if hasattr(language_module, 'hint'): hints = language_module.hint(self.run, program) if not hints and hasattr(problem_module, 'hint'): hints = problem_module.hint(self.run, program) if not hints: hints = [{'id': 'no_hint'}] self._instantiate_and_save_hints(language_module, problem_module, hints) return hints def test(self, sid, problem_id, program): language, problem_group, problem = Problem.get_identifier(problem_id) language_module = problems.load_language(language, 'common') problem_module = problems.load_problem(language, problem_group, problem, 'common') try: n_correct, n_all = problem_module.test(self.run, program) hints = [{'id': 'test_results', 'args': {'passed': n_correct, 'total': n_all}}] except AttributeError as ex: hints = [{'id': 'test_results', 'args': {'passed': 0, 'total': 0}}] self._instantiate_and_save_hints(language_module, problem_module, hints) return hints # Add hint parameters (such as message index) based on hint class. Append # the finalized hints to the list of sent hints. def _instantiate_and_save_hints(self, language_mod, problem_mod, hints): with self._access_lock: for hint in hints: for mod in [language_mod, problem_mod]: if hasattr(mod, 'hint_type') and hint['id'] in mod.hint_type: hint_type = mod.hint_type[hint['id']] hint_type.instantiate(hint, self._sent_hints) self._sent_hints.extend(hints)