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 +11,11 @@ import logging | ||
| 11 | from os import path | 11 | from os import path |
| 12 | import random | 12 | import random |
| 13 | import re | 13 | import re |
| 14 | -from typing import Any, Dict, NewType | 14 | +from typing import Any, Dict, List, NewType |
| 15 | import uuid | 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 | # setup logger for this module | 20 | # setup logger for this module |
| 21 | logger = logging.getLogger(__name__) | 21 | logger = logging.getLogger(__name__) |
| @@ -33,8 +33,9 @@ class QuestionException(Exception): | @@ -33,8 +33,9 @@ class QuestionException(Exception): | ||
| 33 | # ============================================================================ | 33 | # ============================================================================ |
| 34 | class Question(dict): | 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 | Instances can shuffle options or automatically generate questions. | 39 | Instances can shuffle options or automatically generate questions. |
| 39 | ''' | 40 | ''' |
| 40 | 41 | ||
| @@ -695,3 +696,44 @@ class QFactory(): | @@ -695,3 +696,44 @@ class QFactory(): | ||
| 695 | question = question_from(qdict) # returns a Question instance | 696 | question = question_from(qdict) # returns a Question instance |
| 696 | question.gen() | 697 | question.gen() |
| 697 | return question | 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,42 +212,42 @@ def load_yaml(filename: str, default: Any = None) -> Any: | ||
| 212 | # ---------------------------------------------------------------------------- | 212 | # ---------------------------------------------------------------------------- |
| 213 | # Same as above, but asynchronous | 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 |