<commit_before>import math
from IPython.display import display
from learntools.python.globals_binder import binder 
from learntools.python.richtext import *

#XXX: This is probably problematic from a testing point of view...
G = binder.readonly_globals()

def displayer(fn):
    """Decorator taking a function returning a str/RichText object, and 
    making it instead display that value (and return nothing)
    """
    def wrapped(cls, *args):
        res = fn(cls, *args)
        cls.display(res)
        # Don't propagate the return to avoid double printing.
    return wrapped

# TODO: Might want to rethink this Metaclass cleverness. I've tried to structure
# the code such that concrete Problem subclasses will (almost?) never need to directly
# implement check(), is_attempted(), hint(), or solution(). They usually just need
# to define some class attributes. In a complicated case, they might have to define
# their own is_attempted(), or _do_check().
# SO, the automatic wrapping of those methods doesn't really save many lines of code,
# and maybe isn't worth the added crypticness and debugging difficulty.
class ProblemMeta(type):
    """Metaclass for Problems. Does stuff like...
    - Decorates all methods with classmethod
    - wraps certain methods with displayer()
    - wraps certain attrs in corresponding RichText classes (e.g. Hint() around _hint(s))
    """
    # Wrap methods with these names with the displayer decorator
    display_wraps = ['check', 'hint', 'solution',]
    def __new__(meta, name, parents, dct):
        # TODO: I don't think this is really used/needed anymore?
        # Set _varname attr (q1, q2...)
        dct.setdefault('_varname', name)

        # Wrap methods
        for attr, val in dct.items():
            if callable(val):
                if attr in meta.display_wraps:
                    val = displayer(val)
                val = classmethod(val)
                dct[attr] = val

        # Wrap hints
        hints = dct.get('_hints', [])
        for i, hint_text in enumerate(hints):
            assert isinstance(hint_text, str)
            hints[i] = Hint(hint_text, n=i+1, 
                    last = (i == (len(hints)-1))
                    )
        if dct.get('_hint') and not hints:
            hints.append( Hint(dct['_hint']) )
            dct['_hints'] = hints

        return super().__new__(meta, name, parents, dct)


class NotAttempted(Exception):
    pass

class Incorrect(Exception):
    pass


class Problem(object, metaclass=ProblemMeta):
    """Problem objects are what will be presented to the user for (almost) every problem in the 
    exercise notebooks for the Python track. They can be interacted with using the standard methods:
    - check
    - hint
    - solution
    """

    # Subclasses should fill in no more than one of the two attributes below. 
    # _hints is for the case where you want to offer multiple hints (presumably
    # of increasing explicitness). _hint is a convenience for the most common
    # case where you want just one.
    _hint = ''
    _hints = []

    _solution = ''

    # subclasses should define up to 1 of these attrs. Names of variables the
    # user must define to solve the problem. Will be injected into subclass
    # method calls.
    _var = None
    _vars = None

    # TODO: would be nice if this said q1.check() or q2.check() or whatever
    # Probably doable once we tie problems together into "Exercise" containers
    _problem = ("When you've updated the starter code, `check()` will"
            " tell you whether your code is correct."
            )

    # How many times this problem has been attempted (by calling .check())
    _tries = 0
    _hinted = False
    _peeked = False

    def _injectable_vars(cls):
        assert cls._var is None or cls._vars is None, ("Subclass should not implement"
                " both _var and _vars")
        if cls._var:
            names = [cls._var]
        elif cls._vars:
            names = cls._vars
        else:
            names = []
        return names

    def _get_injected_args(cls):
        """Snoop notebook global namespace for variables whose value we should
        inject into calls to check() and is_attempted()"""
        names = cls._injectable_vars()
        missing = set(names) - G.keys()
        if len(missing) == 0:
            return G.lookup(names)
        elif len(missing) == len(names):
            # Hm, maybe RichText objects should be raisable? Or is that too much?
            raise NotAttempted("Remember, you must create the following variable{}: {}"\
                    .format('s' if len(missing) > 1 else '', 
                        ', '.join(missing)
                        )
                    )
        else:
            raise Incorrect("You still need to define the following variables: {}".format(
                missing))

    def check(cls):
        """Check the given answer. 3 possibilities:
        1. If we can detect that the user has probably not attempted the problem
           (i.e. the starter code is unchanged), then 'yellow light'. Remind them
           what they have to do (and that they can get hints/solutions?).
        2. If their answer is wrong, red light. Maybe give some idea of in what
            way their answer is wrong. (Remind about hints/solutions?)
        3. If their answer is right, green light. Congratulations. Possibly some coda.
        """
        cls._tries += 1
        try:
            args = cls._get_injected_args()
            cls._check_whether_attempted(*args)<commit_msg>put backticks around variable names in not_attempted message<commit_after>import math
from IPython.display import display
from learntools.python.globals_binder import binder 
from learntools.python.richtext import *

#XXX: This is probably problematic from a testing point of view...
G = binder.readonly_globals()

def displayer(fn):
    """Decorator taking a function returning a str/RichText object, and 
    making it instead display that value (and return nothing)
    """
    def wrapped(cls, *args):
        res = fn(cls, *args)
        cls.display(res)
        # Don't propagate the return to avoid double printing.
    return wrapped

# TODO: Might want to rethink this Metaclass cleverness. I've tried to structure
# the code such that concrete Problem subclasses will (almost?) never need to directly
# implement check(), is_attempted(), hint(), or solution(). They usually just need
# to define some class attributes. In a complicated case, they might have to define
# their own is_attempted(), or _do_check().
# SO, the automatic wrapping of those methods doesn't really save many lines of code,
# and maybe isn't worth the added crypticness and debugging difficulty.
class ProblemMeta(type):
    """Metaclass for Problems. Does stuff like...
    - Decorates all methods with classmethod
    - wraps certain methods with displayer()
    - wraps certain attrs in corresponding RichText classes (e.g. Hint() around _hint(s))
    """
    # Wrap methods with these names with the displayer decorator
    display_wraps = ['check', 'hint', 'solution',]
    def __new__(meta, name, parents, dct):
        # TODO: I don't think this is really used/needed anymore?
        # Set _varname attr (q1, q2...)
        dct.setdefault('_varname', name)

        # Wrap methods
        for attr, val in dct.items():
            if callable(val):
                if attr in meta.display_wraps:
                    val = displayer(val)
                val = classmethod(val)
                dct[attr] = val

        # Wrap hints
        hints = dct.get('_hints', [])
        for i, hint_text in enumerate(hints):
            assert isinstance(hint_text, str)
            hints[i] = Hint(hint_text, n=i+1, 
                    last = (i == (len(hints)-1))
                    )
        if dct.get('_hint') and not hints:
            hints.append( Hint(dct['_hint']) )
            dct['_hints'] = hints

        return super().__new__(meta, name, parents, dct)


class NotAttempted(Exception):
    pass

class Incorrect(Exception):
    pass


class Problem(object, metaclass=ProblemMeta):
    """Problem objects are what will be presented to the user for (almost) every problem in the 
    exercise notebooks for the Python track. They can be interacted with using the standard methods:
    - check
    - hint
    - solution
    """

    # Subclasses should fill in no more than one of the two attributes below. 
    # _hints is for the case where you want to offer multiple hints (presumably
    # of increasing explicitness). _hint is a convenience for the most common
    # case where you want just one.
    _hint = ''
    _hints = []

    _solution = ''

    # subclasses should define up to 1 of these attrs. Names of variables the
    # user must define to solve the problem. Will be injected into subclass
    # method calls.
    _var = None
    _vars = None

    # TODO: would be nice if this said q1.check() or q2.check() or whatever
    # Probably doable once we tie problems together into "Exercise" containers
    _problem = ("When you've updated the starter code, `check()` will"
            " tell you whether your code is correct."
            )

    # How many times this problem has been attempted (by calling .check())
    _tries = 0
    _hinted = False
    _peeked = False

    def _injectable_vars(cls):
        assert cls._var is None or cls._vars is None, ("Subclass should not implement"
                " both _var and _vars")
        if cls._var:
            names = [cls._var]
        elif cls._vars:
            names = cls._vars
        else:
            names = []
        return names

    def _get_injected_args(cls):
        """Snoop notebook global namespace for variables whose value we should
        inject into calls to check() and is_attempted()"""
        names = cls._injectable_vars()
        missing = set(names) - G.keys()
        if len(missing) == 0:
            return G.lookup(names)
        elif len(missing) == len(names):
            # Hm, maybe RichText objects should be raisable? Or is that too much?
            raise NotAttempted("Remember, you must create the following variable{}: {}"\
                    .format('s' if len(missing) > 1 else '', 
                        ', '.join('`{}`'.format(v) for v in missing)
                        )
                    )
        else:
            raise Incorrect("You still need to define the following variables: {}".format(
                missing))

    def check(cls):
        """Check the given answer. 3 possibilities:
        1. If we can detect that the user has probably not attempted the problem
           (i.e. the starter code is unchanged), then 'yellow light'. Remind them
           what they have to do (and that they can get hints/solutions?).
        2. If their answer is wrong, red light. Maybe give some idea of in what
            way their answer is wrong. (Remind about hints/solutions?)
        3. If their answer is right, green light. Congratulations. Possibly some coda.
        """
        cls._tries += 1
        try:
            args = cls._get_injected_args()
            cls._check_whether_attempted(*args)
{'fraction_non_alphanumeric': 0.08551514609954428, 'fraction_numerical': 0.0026807255830578144, 'mean_word_length': 2.3063515509601182, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 5, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3801700', 'n_tokens_mistral': 3117, 'n_tokens_neox': 2964, 'n_words': 1240}