text: from test import support
  import unittest
  from types import MethodType
  
  def funcattrs(**kwds):
      def decorate(func):
          func.__dict__.update(kwds)
          return func
      return decorate
  
  class MiscDecorators (object):
      @staticmethod
      def author(name):
          def decorate(func):
              func.__dict__['author'] = name
              return func
          return decorate
  
  # -----------------------------------------------
  
  class DbcheckError (Exception):
      def __init__(self, exprstr, func, args, kwds):
          # A real version of this would set attributes here
          Exception.__init__(self, "dbcheck %r failed (func=%s args=%s kwds=%s)" %
                             (exprstr, func, args, kwds))
  
  
  def dbcheck(exprstr, globals=None, locals=None):
      "Decorator to implement debugging assertions"
      def decorate(func):
          expr = compile(exprstr, "dbcheck-%s" % func.__name__, "eval")
          def check(*args, **kwds):
              if not eval(expr, globals, locals):
                  raise DbcheckError(exprstr, func, args, kwds)
              return func(*args, **kwds)
          return check
      return decorate
  
  # -----------------------------------------------
  
  def countcalls(counts):
      "Decorator to count calls to a function"
      def decorate(func):
          func_name = func.__name__
          counts[func_name] = 0
          def call(*args, **kwds):
              counts[func_name] += 1
              return func(*args, **kwds)
          call.__name__ = func_name
          return call
      return decorate
  
  # -----------------------------------------------
  
  def memoize(func):
      saved = {}
      def call(*args):
          try:
              return saved[args]
          except KeyError:
              res = func(*args)
              saved[args] = res
              return res
          except TypeError:
              # Unhashable argument
              return func(*args)
      call.__name__ = func.__name__
      return call
  
  # -----------------------------------------------
  
  class TestDecorators(unittest.TestCase):
  
      def test_single(self):
          class C(object):
              @staticmethod
              def foo(): return 42
          self.assertEqual(C.foo(), 42)
          self.assertEqual(C().foo(), 42)
  
      def check_wrapper_attrs(self, method_wrapper, format_str):
          def func(x):
              return x
          wrapper = method_wrapper(func)
  
          self.assertIs(wrapper.__func__, func)
          self.assertIs(wrapper.__wrapped__, func)
  
          for attr in ('__module__', '__qualname__', '__name__',
                       '__doc__', '__annotations__'):
              self.assertIs(getattr(wrapper, attr),
                            getattr(func, attr))
  
          self.assertEqual(repr(wrapper), format_str.format(func))
          return wrapper
  
      def test_staticmethod(self):
          wrapper = self.check_wrapper_attrs(staticmethod, '<staticmethod({!r})>')
  
          # bpo-43682: Static methods are callable since Python 3.10
          self.assertEqual(wrapper(1), 1)
  
      def test_classmethod(self):
          wrapper = self.check_wrapper_attrs(classmethod, '<classmethod({!r})>')
  
          self.assertRaises(TypeError, wrapper, 1)
  
      def test_dotted(self):
          decorators = MiscDecorators()
          @decorators.author('Cleese')
          def foo(): return 42
          self.assertEqual(foo(), 42)
          self.assertEqual(foo.author, 'Cleese')
  
      def test_argforms(self):
          # A few tests of argument passing, as we use restricted form
          # of expressions for decorators.
  
          def noteargs(*args, **kwds):
              def decorate(func):
                  setattr(func, 'dbval', (args, kwds))
                  return func
              return decorate
  
          args = ( 'Now', 'is', 'the', 'time' )
          kwds = dict(one=1, two=2)
          @noteargs(*args, **kwds)
          def f1(): return 42
          self.assertEqual(f1(), 42)
          self.assertEqual(f1.dbval, (args, kwds))
  
          @noteargs('terry', 'gilliam', eric='idle', john='cleese')
          def f2(): return 84
          self.assertEqual(f2(), 84)
          self.assertEqual(f2.dbval, (('terry', 'gilliam'),
                                       dict(eric='idle', john='cleese')))
  
          @noteargs(1, 2,)
          def f3(): pass
          self.assertEqual(f3.dbval, ((1, 2), {}))
  
      def test_dbcheck(self):
          @dbcheck('args[1] is not None')
          def f(a, b):
              return a + b
          self.assertEqual(f(1, 2), 3)
          self.assertRaises(DbcheckError, f, 1, None)
  
      def test_memoize(self):
          counts = {}
  
          @memoize
          @countcalls(counts)
          def double(x):
              return x * 2
          self.assertEqual(double.__name__, 'double')
  
          self.assertEqual(counts, dict(double=0))
  
          # Only the first call with a given argument bumps the call count:
          #
          self.assertEqual(double(2), 4)
          self.assertEqual(counts['double'], 1)
          self.assertEqual(double(2), 4)
          self.assertEqual(counts['double'], 1)
          self.assertEqual(double(3), 6)
          self.assertEqual(counts['double'], 2)
  
          # Unhashable arguments do not get memoized:
          #
          self.assertEqual(double([10]), [10, 10])
          self.assertEqual(counts['double'], 3)
          self.assertEqual(double([10]), [10, 10])
          self.assertEqual(counts['double'], 4)
  
      def test_errors(self):
  
          # Test SyntaxErrors:
          for stmt in ("x,", "x, y", "x = y", "pass", "import sys"):
              compile(stmt, "test", "exec")  # Sanity check.
              with self.assertRaises(SyntaxError):
                  compile(f"@{stmt}\ndef f(): pass", "test", "exec")
  
          # Test TypeErrors that used to be SyntaxErrors:
          for expr in ("1.+2j", "[1, 2][-1]", "(1, 2)", "True", "...", "None"):
              compile(expr, "test", "eval")  # Sanity check.
              with self.assertRaises(TypeError):
                  exec(f"@{expr}\ndef f(): pass")
  
          def unimp(func):
              raise NotImplementedError
          context = dict(nullval=None, unimp=unimp)
  
          for expr, exc in [ ("undef", NameError),
                             ("nullval", TypeError),
                             ("nullval.attr", AttributeError),
                             ("unimp", NotImplementedError)]:
              codestr = "@%s\ndef f(): pass\nassert f() is None" % expr
              code = compile(codestr, "test", "exec")
              self.assertRaises(exc, eval, code, context)
  
      def test_expressions(self):
          for expr in (
              "(x,)", "(x, y)", "x := y", "(x := y)", "x @y", "(x @ y)", "x[0]",
              "w[x].y.z", "w + x - (y + z)", "x(y)()(z)", "[w, x, y][z]", "x.y",
          ):
              compile(f"@{expr}\ndef f(): pass", "test", "exec")
  
      def test_double(self):
          class C(object):
              @funcattrs(abc=1, xyz="haha")
              @funcattrs(booh=42)
              def foo(self): return 42
          self.assertEqual(C().foo(), 42)
          self.assertEqual(C.foo.abc, 1)
          self.assertEqual(C.foo.xyz, "haha")
          self.assertEqual(C.foo.booh, 42)
  
      def test_order(self):
          # Test that decorators are applied in the proper order to the function
          # they are decorating.
          def callnum(num):
              """Decorator factory that returns a decorator that replaces the
              passed-in function with one that returns the value of 'num'"""
              def deco(func):
                  return lambda: num
              return deco
          @callnum(2)
          @callnum(1)
          def foo(): return 42
          self.assertEqual(foo(), 2,
                              "Application order of decorators is incorrect")
  
      def test_eval_order(self):
          # Evaluating a decorated function involves four steps for each
          # decorator-maker (the function that returns a decorator):
          #
          #    1: Evaluate the decorator-maker name
          #    2: Evaluate the decorator-maker arguments (if any)
          #    3: Call the decorator-maker to make a decorator
          #    4: Call the decorator
          #
          # When there are multiple decorators, these steps should be
          # performed in the above order for each decorator, but we should
          # iterate through the decorators in the reverse of the order they
          # appear in the source.
  
          actions = []
  
          def make_decorator(tag):
              actions.append('makedec' + tag)
              def decorate(func):
                  actions.append('calldec' + tag)
                  return func
              return decorate
  
          class NameLookupTracer (object):
              def __init__(self, index):
                  self.index = index
  
              def __getattr__(self, fname):
                  if fname == 'make_decorator':
                      opname, res = ('evalname', make_decorator)
                  elif fname == 'arg':
                      opname, res = ('evalargs', str(self.index))
                  else:
                      assert False, "Unknown attrname %s" % fname
                  actions.append('%s%d' % (opname, self.index))
                  return res
  
          c1, c2, c3 = map(NameLookupTracer, [ 1, 2, 3 ])
  
          expected_actions = [ 'evalname1', 'evalargs1', 'makedec1',
                               'evalname2', 'evalargs2', 'makedec2',
                               'evalname3', 'evalargs3', 'makedec3',
                               'calldec3', 'calldec2', 'calldec1' ]
  
          actions = []
          @c1.make_decorator(c1.arg)
          @c2.make_decorator(c2.arg)
          @c3.make_decorator(c3.arg)
          def foo(): return 42
          self.assertEqual(foo(), 42)
  
          self.assertEqual(actions, expected_actions)
  
          # Test the equivalence claim in chapter 7 of the reference manual.
          #
          actions = []
          def bar(): return 42
          bar = c1.make_decorator(c1.arg)(c2.make_decorator(c2.arg)(c3.make_decorator(c3.arg)(bar)))
          self.assertEqual(bar(), 42)
          self.assertEqual(actions, expected_actions)
  
      def test_wrapped_descriptor_inside_classmethod(self):
          class BoundWrapper:
              def __init__(self, wrapped):
                  self.__wrapped__ = wrapped
  
              def __call__(self, *args, **kwargs):
                  return self.__wrapped__(*args, **kwargs)
  
          class Wrapper:
              def __init__(self, wrapped):
                  self.__wrapped__ = wrapped
  
              def __get__(self, instance, owner):
                  bound_function = self.__wrapped__.__get__(instance, owner)
                  return BoundWrapper(bound_function)
  
          def decorator(wrapped):
              return Wrapper(wrapped)
  
          class Class:
              @decorator
              @classmethod
              def inner(cls):
                  # This should already work.
                  return 'spam'
  
              @classmethod
              @decorator
              def outer(cls):
                  # Raised TypeError with a message saying that the 'Wrapper'
                  # object is not callable.
                  return 'eggs'
  
          self.assertEqual(Class.inner(), 'spam')
          self.assertEqual(Class.outer(), 'eggs')
          self.assertEqual(Class().inner(), 'spam')
          self.assertEqual(Class().outer(), 'eggs')
  
      def test_wrapped_classmethod_inside_classmethod(self):
          class MyClassMethod1:
              def __init__(self, func):
                  self.func = func
  
              def __call__(self, cls):
                  if hasattr(self.func, '__get__'):
                      return self.func.__get__(cls, cls)()
                  return self.func(cls)
  
              def __get__(self, instance, owner=None):
                  if owner is None:
                      owner = type(instance)
                  return MethodType(self, owner)
  
          class MyClassMethod2:
              def __init__(self, func):
                  if isinstance(func, classmethod):
                      func = func.__func__
                  self.func = func
  
              def __call__(self, cls):
                  return self.func(cls)
  
              def __get__(self, instance, owner=None):
                  if owner is None:
                      owner = type(instance)
                  return MethodType(self, owner)
  
          for myclassmethod in [MyClassMethod1, MyClassMethod2]:
              class A:
                  @myclassmethod
                  def f1(cls):
                      return cls
  
                  @classmethod
                  @myclassmethod
                  def f2(cls):
                      return cls
  
                  @myclassmethod
                  @classmethod
                  def f3(cls):
                      return cls
  
                  @classmethod
                  @classmethod
                  def f4(cls):
                      return cls
  
                  @myclassmethod
                  @MyClassMethod1
                  def f5(cls):
                      return cls
  
                  @myclassmethod
                  @MyClassMethod2
                  def f6(cls):
                      return cls
  
              self.assertIs(A.f1(), A)
              self.assertIs(A.f2(), A)
              self.assertIs(A.f3(), A)
              self.assertIs(A.f4(), A)
              self.assertIs(A.f5(), A)
              self.assertIs(A.f6(), A)
              a = A()
              self.assertIs(a.f1(), A)
              self.assertIs(a.f2(), A)
              self.assertIs(a.f3(), A)
              self.assertIs(a.f4(), A)
              self.assertIs(a.f5(), A)
              self.assertIs(a.f6(), A)
  
              def f(cls):
                  return cls
  
              self.assertIs(myclassmethod(f).__get__(a)(), A)
              self.assertIs(myclassmethod(f).__get__(a, A)(), A)
              self.assertIs(myclassmethod(f).__get__(A, A)(), A)
              self.assertIs(myclassmethod(f).__get__(A)(), type(A))
              self.assertIs(classmethod(f).__get__(a)(), A)
              self.assertIs(classmethod(f).__get__(a, A)(), A)
              self.assertIs(classmethod(f).__get__(A, A)(), A)
              self.assertIs(classmethod(f).__get__(A)(), type(A))
  
  class TestClassDecorators(unittest.TestCase):
  
      def test_simple(self):
          def plain(x):
              x.extra = 'Hello'
              return x
          @plain
          class C(object): pass
          self.assertEqual(C.extra, 'Hello')
  
      def test_double(self):
          def ten(x):
              x.extra = 10
              return x
          def add_five(x):
              x.extra += 5
              return x
  
          @add_five
          @ten
          class C(object): pass
          self.assertEqual(C.extra, 15)
  
      def test_order(self):
          def applied_first(x):
              x.extra = 'first'
              return x
          def applied_second(x):
              x.extra = 'second'
              return x
          @applied_second
          @applied_first
          class C(object): pass
          self.assertEqual(C.extra, 'second')
  
  if __name__ == "__main__":
      unittest.main()
  