Commit 9b8820e43d7c164f7cef3372224d6b9d6975ecef
1 parent
a7d6b9e1
Exists in
dev
move run_script to questions.py
it was only used there.
Showing
2 changed files
with
86 additions
and
44 deletions
Show diff stats
aprendizations/questions.py
| ... | ... | @@ -11,11 +11,11 @@ import logging |
| 11 | 11 | from os import path |
| 12 | 12 | import random |
| 13 | 13 | import re |
| 14 | -from typing import Any, Dict, NewType | |
| 14 | +from typing import Any, Dict, List, NewType | |
| 15 | 15 | import uuid |
| 16 | 16 | |
| 17 | -# this project | |
| 18 | -from aprendizations.tools import run_script | |
| 17 | +# third party libraries | |
| 18 | +import yaml | |
| 19 | 19 | |
| 20 | 20 | # setup logger for this module |
| 21 | 21 | logger = logging.getLogger(__name__) |
| ... | ... | @@ -33,8 +33,9 @@ class QuestionException(Exception): |
| 33 | 33 | # ============================================================================ |
| 34 | 34 | class Question(dict): |
| 35 | 35 | ''' |
| 36 | - Classes derived from this base class are meant to instantiate questions | |
| 37 | - for each student. | |
| 36 | + Base class for all question types | |
| 37 | + | |
| 38 | + Classes derived from this base class can instantiate questions. | |
| 38 | 39 | Instances can shuffle options or automatically generate questions. |
| 39 | 40 | ''' |
| 40 | 41 | |
| ... | ... | @@ -695,3 +696,44 @@ class QFactory(): |
| 695 | 696 | question = question_from(qdict) # returns a Question instance |
| 696 | 697 | question.gen() |
| 697 | 698 | return question |
| 699 | + | |
| 700 | +# ----------------------------------------------------------------------------- | |
| 701 | +async def run_script(script: str, | |
| 702 | + args: List[str], | |
| 703 | + stdin: str = '', | |
| 704 | + timeout: int = 5) -> Any: | |
| 705 | + | |
| 706 | + # normalize args | |
| 707 | + script = path.expanduser(script) | |
| 708 | + input_bytes = stdin.encode('utf-8') | |
| 709 | + args = [str(a) for a in args] | |
| 710 | + | |
| 711 | + try: | |
| 712 | + p = await asyncio.create_subprocess_exec( | |
| 713 | + script, *args, | |
| 714 | + stdin=asyncio.subprocess.PIPE, | |
| 715 | + stdout=asyncio.subprocess.PIPE, | |
| 716 | + stderr=asyncio.subprocess.DEVNULL, | |
| 717 | + ) | |
| 718 | + except FileNotFoundError: | |
| 719 | + logger.error(f'Can not execute script "{script}": not found.') | |
| 720 | + except PermissionError: | |
| 721 | + logger.error(f'Can not execute script "{script}": wrong permissions.') | |
| 722 | + except OSError: | |
| 723 | + logger.error(f'Can not execute script "{script}": unknown reason.') | |
| 724 | + else: | |
| 725 | + try: | |
| 726 | + stdout, _ = await asyncio.wait_for(p.communicate(input_bytes), timeout) | |
| 727 | + except asyncio.TimeoutError: | |
| 728 | + logger.warning(f'Timeout {timeout}s exceeded running "{script}".') | |
| 729 | + return | |
| 730 | + | |
| 731 | + if p.returncode != 0: | |
| 732 | + logger.error(f'Return code {p.returncode} running "{script}".') | |
| 733 | + else: | |
| 734 | + try: | |
| 735 | + output = yaml.safe_load(stdout.decode('utf-8', 'ignore')) | |
| 736 | + except yaml.YAMLError: | |
| 737 | + logger.error(f'Error parsing yaml output of "{script}"') | |
| 738 | + else: | |
| 739 | + return output | ... | ... |
aprendizations/tools.py
| ... | ... | @@ -212,42 +212,42 @@ def load_yaml(filename: str, default: Any = None) -> Any: |
| 212 | 212 | # ---------------------------------------------------------------------------- |
| 213 | 213 | # Same as above, but asynchronous |
| 214 | 214 | # ---------------------------------------------------------------------------- |
| 215 | -async def run_script(script: str, | |
| 216 | - args: List[str] = [], | |
| 217 | - stdin: str = '', | |
| 218 | - timeout: int = 2) -> Any: | |
| 219 | - | |
| 220 | - # normalize args | |
| 221 | - script = path.expanduser(script) | |
| 222 | - input_bytes = stdin.encode('utf-8') | |
| 223 | - args = [str(a) for a in args] | |
| 224 | - | |
| 225 | - try: | |
| 226 | - p = await asyncio.create_subprocess_exec( | |
| 227 | - script, *args, | |
| 228 | - stdin=asyncio.subprocess.PIPE, | |
| 229 | - stdout=asyncio.subprocess.PIPE, | |
| 230 | - stderr=asyncio.subprocess.DEVNULL, | |
| 231 | - ) | |
| 232 | - except FileNotFoundError: | |
| 233 | - logger.error(f'Can not execute script "{script}": not found.') | |
| 234 | - except PermissionError: | |
| 235 | - logger.error(f'Can not execute script "{script}": wrong permissions.') | |
| 236 | - except OSError: | |
| 237 | - logger.error(f'Can not execute script "{script}": unknown reason.') | |
| 238 | - else: | |
| 239 | - try: | |
| 240 | - stdout, _ = await asyncio.wait_for(p.communicate(input_bytes), timeout) | |
| 241 | - except asyncio.TimeoutError: | |
| 242 | - logger.warning(f'Timeout {timeout}s exceeded running "{script}".') | |
| 243 | - return | |
| 244 | - | |
| 245 | - if p.returncode != 0: | |
| 246 | - logger.error(f'Return code {p.returncode} running "{script}".') | |
| 247 | - else: | |
| 248 | - try: | |
| 249 | - output = yaml.safe_load(stdout.decode('utf-8', 'ignore')) | |
| 250 | - except Exception: | |
| 251 | - logger.error(f'Error parsing yaml output of "{script}"') | |
| 252 | - else: | |
| 253 | - return output | |
| 215 | +# async def run_script(script: str, | |
| 216 | +# args: List[str] = [], | |
| 217 | +# stdin: str = '', | |
| 218 | +# timeout: int = 5) -> Any: | |
| 219 | +# | |
| 220 | +# # normalize args | |
| 221 | +# script = path.expanduser(script) | |
| 222 | +# input_bytes = stdin.encode('utf-8') | |
| 223 | +# args = [str(a) for a in args] | |
| 224 | +# | |
| 225 | +# try: | |
| 226 | +# p = await asyncio.create_subprocess_exec( | |
| 227 | +# script, *args, | |
| 228 | +# stdin=asyncio.subprocess.PIPE, | |
| 229 | +# stdout=asyncio.subprocess.PIPE, | |
| 230 | +# stderr=asyncio.subprocess.DEVNULL, | |
| 231 | +# ) | |
| 232 | +# except FileNotFoundError: | |
| 233 | +# logger.error(f'Can not execute script "{script}": not found.') | |
| 234 | +# except PermissionError: | |
| 235 | +# logger.error(f'Can not execute script "{script}": wrong permissions.') | |
| 236 | +# except OSError: | |
| 237 | +# logger.error(f'Can not execute script "{script}": unknown reason.') | |
| 238 | +# else: | |
| 239 | +# try: | |
| 240 | +# stdout, _ = await asyncio.wait_for(p.communicate(input_bytes), timeout) | |
| 241 | +# except asyncio.TimeoutError: | |
| 242 | +# logger.warning(f'Timeout {timeout}s exceeded running "{script}".') | |
| 243 | +# return | |
| 244 | +# | |
| 245 | +# if p.returncode != 0: | |
| 246 | +# logger.error(f'Return code {p.returncode} running "{script}".') | |
| 247 | +# else: | |
| 248 | +# try: | |
| 249 | +# output = yaml.safe_load(stdout.decode('utf-8', 'ignore')) | |
| 250 | +# except Exception: | |
| 251 | +# logger.error(f'Error parsing yaml output of "{script}"') | |
| 252 | +# else: | |
| 253 | +# return output | ... | ... |