-
Notifications
You must be signed in to change notification settings - Fork 14
Expand file tree
/
Copy pathscripts_commons.py
More file actions
123 lines (94 loc) · 4.26 KB
/
scripts_commons.py
File metadata and controls
123 lines (94 loc) · 4.26 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
"""Helpers used by pythonanywhere helper scripts."""
import logging
import sys
from schema import And, Or, Schema, SchemaError, Use
from snakesay import snakesay
from pythonanywhere.task import Task
logger = logging.getLogger(__name__)
# fmt: off
tabulate_formats = [
"plain", "simple", "github", "grid", "fancy_grid", "pipe", "orgtbl", "jira",
"presto", "psql", "rst", "mediawiki", "moinmoin", "youtrack", "html", "latex",
"latex_raw", "latex_booktabs", "textile",
]
# fmt: on
class ScriptSchema(Schema):
"""Extends `Schema` adapting it to PA scripts validation strategies.
Adds predefined schemata as class variables to be used in scripts'
validation schemas as well as `validate_user_input` method which acts
as `Schema.validate` but returns a dictionary with converted keys
ready to be used as function keyword arguments, e.g. validated
arguments {"--foo": bar, "<baz>": qux} will be be converted to
{"foo": bar, "baz": qux}. Additional conversion rules may be added as
dictionary passed to `validate_user_input` :method: as `conversions`
:param:.
Use :method:`ScriptSchema.validate_user_input` to obtain kwarg
dictionary."""
# class variables are used in task scripts schemata:
boolean = Or(None, bool)
hour = Or(None, And(Use(int), lambda h: 0 <= h <= 23), error="--hour has to be in 0..23")
id_multi = Or([], And(lambda y: [x.isdigit() for x in y], error="<id> has to be integer"))
id_required = And(Use(int), error="<id> has to be an integer")
minute_required = And(Use(int), lambda m: 0 <= m <= 59, error="--minute has to be in 0..59")
minute = Or(None, minute_required)
string = Or(None, str)
tabulate_format = Or(
None,
And(str, lambda f: f in tabulate_formats),
error=f"--format should match one of: {', '.join(tabulate_formats)}",
)
replacements = {"--": "", "<": "", ">": ""}
def convert(self, string):
"""Removes cli argument notation characters ('--', '<', '>' etc.).
:param string: cli argument key to be converted to fit Python
argument syntax."""
for key, value in self.replacements.items():
string = string.replace(key, value)
return string
def validate_user_input(self, arguments, *, conversions=None):
"""Calls `Schema.validate` on provided `arguments`.
Returns dictionary with keys converted by
`ScriptSchema.convert` :method: to be later used as kwarg
arguments. Universal rules for conversion are stored in
`replacements` class variable and may be updated using
`conversions` kwarg. Use optional `conversions` :param: to add
custom replacement rules.
:param arguments: dictionary of cli arguments provided be
(e.g.) `docopt`
:param conversions: dictionary of additional rules to
`self.replacements`"""
if conversions:
self.replacements.update(conversions)
try:
self.validate(arguments)
return {self.convert(key): val for key, val in arguments.items()}
except SchemaError as e:
logger.warning(snakesay(str(e)))
sys.exit(1)
def get_logger(set_info=False):
"""Sets logger for 'pythonanywhere' package.
Returns `logging.Logger` instance with no message formatting which
will stream to stdout. With `set_info` :param: set to `True`
logger defines `logging.INFO` level otherwise it leaves default
`logging.WARNING`.
To toggle message visibility in scripts use `logger.info` calls
and switch `set_info` value accordingly.
:param set_info: boolean (defaults to False)"""
logging.basicConfig(format="%(message)s", stream=sys.stdout)
logger = logging.getLogger("pythonanywhere")
if set_info:
logger.setLevel(logging.INFO)
else:
logger.setLevel(logging.WARNING)
return logger
def get_task_from_id(task_id, no_exit=False):
"""Get `Task.from_id` instance representing existing task.
:param task_id: integer (should be a valid task id)
:param no_exit: if (default) False sys.exit will be called when
exception is caught"""
try:
return Task.from_id(task_id)
except Exception as e:
logger.warning(snakesay(str(e)))
if not no_exit:
sys.exit(1)