Skip to content

Commit c7ff47d

Browse files
authored
refactored config into its own file
1 parent 3979873 commit c7ff47d

File tree

3 files changed

+82
-36
lines changed

3 files changed

+82
-36
lines changed

check50/__init__.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -41,15 +41,15 @@ def _setup_translation():
4141
run,
4242
log, _log,
4343
hidden,
44-
Failure, Mismatch, Missing, Config,
45-
configure
44+
Failure, Mismatch, Missing,
4645
)
4746

4847

4948
from . import regex
5049
from .runner import check
50+
from .config import config
5151
from pexpect import EOF
5252

5353
__all__ = ["import_checks", "data", "exists", "hash", "include", "regex",
5454
"run", "log", "Failure", "Mismatch", "Missing", "check", "EOF",
55-
"Config", "configure"]
55+
"config"]

check50/_api.py

Lines changed: 2 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
from pexpect.exceptions import EOF, TIMEOUT
1212

1313
from . import internal, regex
14+
from .config import config
1415

1516
_log = []
1617
internal.register.before_every(_log.clear)
@@ -469,38 +470,6 @@ def _safe_truncate(x, y):
469470

470471
self.payload.update({"expected": expected, "actual": actual})
471472

472-
473-
class Config:
474-
"""
475-
Configuration for check50 behavior.
476-
477-
This class stores user-defined configuration options (currently only
478-
truncation length) that influence check50’s output formatting.
479-
"""
480-
def __init__(self):
481-
self.truncate_len = 10
482-
self.dynamic_truncate = True
483-
484-
config = Config()
485-
486-
def configure(truncate_len=None):
487-
"""
488-
Configure check50 behavior.
489-
490-
By default, check50 truncates strings around their first point of difference.
491-
However, if the user specifies a custom `truncate_len` via `check50.configure`,
492-
then string outputs will be sliced from the beginning instead.
493-
494-
Example usage::
495-
import check50
496-
check50.configure(truncate_len=15)
497-
"""
498-
if truncate_len:
499-
if not isinstance(truncate_len, int) or truncate_len < 1:
500-
raise ValueError("truncation length must be a positive integer")
501-
config.truncate_len = truncate_len
502-
config.dynamic_truncate = False
503-
504473
def hidden(failure_rationale):
505474
"""
506475
Decorator that marks a check as a 'hidden' check. This will suppress the log
@@ -530,7 +499,7 @@ def wrapper(*args, **kwargs):
530499
return wrapper
531500
return decorator
532501

533-
def _truncate(s, other, max_len=10):
502+
def _truncate(s, other):
534503
def normalize(obj):
535504
if isinstance(obj, list):
536505
return "\n".join(map(str, obj))

check50/config.py

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
class Config:
2+
"""
3+
Configuration for `check50` behavior.
4+
5+
This class stores user-defined configuration options that influence
6+
check50's output formatting.
7+
8+
For developers of `check50`, you can extend the `Config` class by adding new
9+
variables to the `__init__`, which will automatically generate new "setter"
10+
functions to modify the default values. Additionally, if the new
11+
configuration needs to be validated before the user can modify it, add your
12+
validation into the `_validators` dictionary.
13+
"""
14+
def __init__(self):
15+
self.truncate_len = 10
16+
self.dynamic_truncate = True
17+
18+
# Create boolean validators for your variables here (if needed):
19+
# A help message is not required.
20+
self._validators = {
21+
"truncate_len": (lambda val: isinstance(val, int) and val >= 1,
22+
"truncate_len must be a positive integer"),
23+
}
24+
25+
# Dynamically generates setter functions based on variable names and
26+
# the type of the default values
27+
self._generate_setters()
28+
29+
def _generate_setters(self):
30+
def create_method(name, func):
31+
setattr(self.__class__, name, func)
32+
33+
def make_toggle(attr):
34+
"""Factory for making functions like `toggle_<attr_name>()`"""
35+
def toggler(self):
36+
setattr(self, attr, not getattr(self, attr))
37+
return toggler
38+
39+
def make_setter(attr):
40+
"""Factory for making functions like `set_<attr_name>(arg)`"""
41+
def setter(self, value):
42+
# Get the entry in the dict of validators.
43+
# Check to see if the value passes the validator, and if it
44+
# didn't, display the help message, if any.
45+
validator_entry = self._validators.get(attr)
46+
47+
if validator_entry:
48+
if isinstance(validator_entry, tuple):
49+
validator, help = validator_entry
50+
else:
51+
validator, help = validator_entry, None
52+
53+
if not validator(value):
54+
error_msg = f"invalid value for {attr}: {value}"
55+
if help:
56+
error_msg += f", {help}"
57+
raise ValueError(error_msg)
58+
59+
setattr(self, attr, value)
60+
return setter
61+
62+
for attribute_name in dir(self):
63+
if attribute_name.startswith('_'):
64+
continue # skip "private" attributes (denoted with a prefix `_`)
65+
value = getattr(self, attribute_name)
66+
if callable(value):
67+
continue # skip functions/methods
68+
69+
# For variables with the default boolean type, make a setter that
70+
# starts with `toggle_`. Otherwise, have it start with `set_`.
71+
if isinstance(value, bool):
72+
create_method(f"toggle_{attribute_name}", make_toggle(attribute_name))
73+
else:
74+
create_method(f"set_{attribute_name}", make_setter(attribute_name))
75+
76+
77+
config = Config()

0 commit comments

Comments
 (0)