--- /dev/null
- #!/usr/bin/env python3
+"""
+Operator Interface
+
+This module exports a set of functions corresponding to the intrinsic
+operators of Python. For example, operator.add(x, y) is equivalent
+to the expression x+y. The function names are those used for special
+methods; variants without leading and trailing '__' are also provided
+for convenience.
+
+This is the pure Python implementation of the module.
+"""
+
+__all__ = ['abs', 'add', 'and_', 'attrgetter', 'concat', 'contains', 'countOf',
+ 'delitem', 'eq', 'floordiv', 'ge', 'getitem', 'gt', 'iadd', 'iand',
+ 'iconcat', 'ifloordiv', 'ilshift', 'imod', 'imul', 'index',
+ 'indexOf', 'inv', 'invert', 'ior', 'ipow', 'irshift', 'is_',
+ 'is_not', 'isub', 'itemgetter', 'itruediv', 'ixor', 'le',
+ 'length_hint', 'lshift', 'lt', 'methodcaller', 'mod', 'mul', 'ne',
+ 'neg', 'not_', 'or_', 'pos', 'pow', 'rshift', 'setitem', 'sub',
+ 'truediv', 'truth', 'xor']
+
+from builtins import abs as _abs
+
+
+# Comparison Operations *******************************************************#
+
+def lt(a, b):
+ "Same as a < b."
+ return a < b
+
+def le(a, b):
+ "Same as a <= b."
+ return a <= b
+
+def eq(a, b):
+ "Same as a == b."
+ return a == b
+
+def ne(a, b):
+ "Same as a != b."
+ return a != b
+
+def ge(a, b):
+ "Same as a >= b."
+ return a >= b
+
+def gt(a, b):
+ "Same as a > b."
+ return a > b
+
+# Logical Operations **********************************************************#
+
+def not_(a):
+ "Same as not a."
+ return not a
+
+def truth(a):
+ "Return True if a is true, False otherwise."
+ return True if a else False
+
+def is_(a, b):
+ "Same as a is b."
+ return a is b
+
+def is_not(a, b):
+ "Same as a is not b."
+ return a is not b
+
+# Mathematical/Bitwise Operations *********************************************#
+
+def abs(a):
+ "Same as abs(a)."
+ return _abs(a)
+
+def add(a, b):
+ "Same as a + b."
+ return a + b
+
+def and_(a, b):
+ "Same as a & b."
+ return a & b
+
+def floordiv(a, b):
+ "Same as a // b."
+ return a // b
+
+def index(a):
+ "Same as a.__index__()."
+ return a.__index__()
+
+def inv(a):
+ "Same as ~a."
+ return ~a
+invert = inv
+
+def lshift(a, b):
+ "Same as a << b."
+ return a << b
+
+def mod(a, b):
+ "Same as a % b."
+ return a % b
+
+def mul(a, b):
+ "Same as a * b."
+ return a * b
+
+def neg(a):
+ "Same as -a."
+ return -a
+
+def or_(a, b):
+ "Same as a | b."
+ return a | b
+
+def pos(a):
+ "Same as +a."
+ return +a
+
+def pow(a, b):
+ "Same as a ** b."
+ return a ** b
+
+def rshift(a, b):
+ "Same as a >> b."
+ return a >> b
+
+def sub(a, b):
+ "Same as a - b."
+ return a - b
+
+def truediv(a, b):
+ "Same as a / b."
+ return a / b
+
+def xor(a, b):
+ "Same as a ^ b."
+ return a ^ b
+
+# Sequence Operations *********************************************************#
+
+def concat(a, b):
+ "Same as a + b, for a and b sequences."
+ if not hasattr(a, '__getitem__'):
+ msg = "'%s' object can't be concatenated" % type(a).__name__
+ raise TypeError(msg)
+ return a + b
+
+def contains(a, b):
+ "Same as b in a (note reversed operands)."
+ return b in a
+
+def countOf(a, b):
+ "Return the number of times b occurs in a."
+ count = 0
+ for i in a:
+ if i == b:
+ count += 1
+ return count
+
+def delitem(a, b):
+ "Same as del a[b]."
+ del a[b]
+
+def getitem(a, b):
+ "Same as a[b]."
+ return a[b]
+
+def indexOf(a, b):
+ "Return the first index of b in a."
+ for i, j in enumerate(a):
+ if j == b:
+ return i
+ else:
+ raise ValueError('sequence.index(x): x not in sequence')
+
+def setitem(a, b, c):
+ "Same as a[b] = c."
+ a[b] = c
+
+def length_hint(obj, default=0):
+ """
+ Return an estimate of the number of items in obj.
+ This is useful for presizing containers when building from an iterable.
+
+ If the object supports len(), the result will be exact. Otherwise, it may
+ over- or under-estimate by an arbitrary amount. The result will be an
+ integer >= 0.
+ """
+ if not isinstance(default, int):
+ msg = ("'%s' object cannot be interpreted as an integer" %
+ type(default).__name__)
+ raise TypeError(msg)
+
+ try:
+ return len(obj)
+ except TypeError:
+ pass
+
+ try:
+ hint = type(obj).__length_hint__
+ except AttributeError:
+ return default
+
+ try:
+ val = hint(obj)
+ except TypeError:
+ return default
+ if val is NotImplemented:
+ return default
+ if not isinstance(val, int):
+ msg = ('__length_hint__ must be integer, not %s' %
+ type(val).__name__)
+ raise TypeError(msg)
+ if val < 0:
+ msg = '__length_hint__() should return >= 0'
+ raise ValueError(msg)
+ return val
+
+# Generalized Lookup Objects **************************************************#
+
+class attrgetter:
+ """
+ Return a callable object that fetches the given attribute(s) from its operand.
+ After f = attrgetter('name'), the call f(r) returns r.name.
+ After g = attrgetter('name', 'date'), the call g(r) returns (r.name, r.date).
+ After h = attrgetter('name.first', 'name.last'), the call h(r) returns
+ (r.name.first, r.name.last).
+ """
+ def __init__(self, attr, *attrs):
+ if not attrs:
+ if not isinstance(attr, str):
+ raise TypeError('attribute name must be a string')
+ names = attr.split('.')
+ def func(obj):
+ for name in names:
+ obj = getattr(obj, name)
+ return obj
+ self._call = func
+ else:
+ getters = tuple(map(attrgetter, (attr,) + attrs))
+ def func(obj):
+ return tuple(getter(obj) for getter in getters)
+ self._call = func
+
+ def __call__(self, obj):
+ return self._call(obj)
+
+class itemgetter:
+ """
+ Return a callable object that fetches the given item(s) from its operand.
+ After f = itemgetter(2), the call f(r) returns r[2].
+ After g = itemgetter(2, 5, 3), the call g(r) returns (r[2], r[5], r[3])
+ """
+ def __init__(self, item, *items):
+ if not items:
+ def func(obj):
+ return obj[item]
+ self._call = func
+ else:
+ items = (item,) + items
+ def func(obj):
+ return tuple(obj[i] for i in items)
+ self._call = func
+
+ def __call__(self, obj):
+ return self._call(obj)
+
+class methodcaller:
+ """
+ Return a callable object that calls the given method on its operand.
+ After f = methodcaller('name'), the call f(r) returns r.name().
+ After g = methodcaller('name', 'date', foo=1), the call g(r) returns
+ r.name('date', foo=1).
+ """
+
+ def __init__(*args, **kwargs):
+ if len(args) < 2:
+ msg = "methodcaller needs at least one argument, the method name"
+ raise TypeError(msg)
+ self = args[0]
+ self._name = args[1]
+ self._args = args[2:]
+ self._kwargs = kwargs
+
+ def __call__(self, obj):
+ return getattr(obj, self._name)(*self._args, **self._kwargs)
+
+# In-place Operations *********************************************************#
+
+def iadd(a, b):
+ "Same as a += b."
+ a += b
+ return a
+
+def iand(a, b):
+ "Same as a &= b."
+ a &= b
+ return a
+
+def iconcat(a, b):
+ "Same as a += b, for a and b sequences."
+ if not hasattr(a, '__getitem__'):
+ msg = "'%s' object can't be concatenated" % type(a).__name__
+ raise TypeError(msg)
+ a += b
+ return a
+
+def ifloordiv(a, b):
+ "Same as a //= b."
+ a //= b
+ return a
+
+def ilshift(a, b):
+ "Same as a <<= b."
+ a <<= b
+ return a
+
+def imod(a, b):
+ "Same as a %= b."
+ a %= b
+ return a
+
+def imul(a, b):
+ "Same as a *= b."
+ a *= b
+ return a
+
+def ior(a, b):
+ "Same as a |= b."
+ a |= b
+ return a
+
+def ipow(a, b):
+ "Same as a **= b."
+ a **=b
+ return a
+
+def irshift(a, b):
+ "Same as a >>= b."
+ a >>= b
+ return a
+
+def isub(a, b):
+ "Same as a -= b."
+ a -= b
+ return a
+
+def itruediv(a, b):
+ "Same as a /= b."
+ a /= b
+ return a
+
+def ixor(a, b):
+ "Same as a ^= b."
+ a ^= b
+ return a
+
+
+try:
+ from _operator import *
+except ImportError:
+ pass
+else:
+ from _operator import __doc__
+
+# All of these "__func__ = func" assignments have to happen after importing
+# from _operator to make sure they're set to the right function
+__lt__ = lt
+__le__ = le
+__eq__ = eq
+__ne__ = ne
+__ge__ = ge
+__gt__ = gt
+__not__ = not_
+__abs__ = abs
+__add__ = add
+__and__ = and_
+__floordiv__ = floordiv
+__index__ = index
+__inv__ = inv
+__invert__ = invert
+__lshift__ = lshift
+__mod__ = mod
+__mul__ = mul
+__neg__ = neg
+__or__ = or_
+__pos__ = pos
+__pow__ = pow
+__rshift__ = rshift
+__sub__ = sub
+__truediv__ = truediv
+__xor__ = xor
+__concat__ = concat
+__contains__ = contains
+__delitem__ = delitem
+__getitem__ = getitem
+__setitem__ = setitem
+__iadd__ = iadd
+__iand__ = iand
+__iconcat__ = iconcat
+__ifloordiv__ = ifloordiv
+__ilshift__ = ilshift
+__imod__ = imod
+__imul__ = imul
+__ior__ = ior
+__ipow__ = ipow
+__irshift__ = irshift
+__isub__ = isub
+__itruediv__ = itruediv
+__ixor__ = ixor
- Issue #20113: os.readv() and os.writev() now raise an OSError exception on
error instead of returning -1.
+- Issue #19719: Make importlib.abc.MetaPathFinder.find_module(),
+ PathEntryFinder.find_loader(), and Loader.load_module() use PEP 451 APIs to
+ help with backwards-compatibility.
+
+- Issue #20144: inspect.Signature now supports parsing simple symbolic
+ constants as parameter default values in __text_signature__.
+
- Issue #20072: Fixed multiple errors in tkinter with wantobjects is False.
+- Issue #20229: Avoid plistlib deprecation warning in platform.mac_ver().
+
+- Issue #14455: Fix some problems with the new binary plist support in plistlib.
+
+IDLE
+----
+
+- Issue #18960: IDLE now ignores the source encoding declaration on the second
+ line if the first line contains anything except a comment.
+
+Tests
+-----
+
+- Issue #19886: Use better estimated memory requirements for bigmem tests.
+
+Tools/Demos
+-----------
+
++- Issue #19936: Added executable bits or shebang lines to Python scripts which
++ requires them. Disable executable bits and shebang lines in test and
++ benchmark files in order to prevent using a random system python, and in
++ source files of modules which don't provide command line interface. Fixed
++ shebang lines in the unittestgui and checkpip scripts.
++
+- Issue #20268: Argument Clinic now supports cloning the parameters and
+ return converter of existing functions.
+
+- Issue #20228: Argument Clinic now has special support for class special
+ methods.
+
+- Issue #20214: Fixed a number of small issues and documentation errors in
+ Argument Clinic (see issue for details).
+
+- Issue #20196: Fixed a bug where Argument Clinic did not generate correct
+ parsing code for functions with positional-only parameters where all arguments
+ are optional.
+
+- Issue #18960: 2to3 and the findnocoding.py script now ignore the source
+ encoding declaration on the second line if the first line contains anything
+ except a comment.
+
+- Issue #19723: The marker comments Argument Clinic uses have been changed
+ to improve readability.
+
+- Issue #20157: When Argument Clinic renames a parameter because its name
+ collides with a C keyword, it no longer exposes that rename to PyArg_Parse.
+
+- Issue #20141: Improved Argument Clinic's support for the PyArg_Parse "O!"
+ format unit.
+
+- Issue #20144: Argument Clinic now supports simple symbolic constants
+ as parameter default values.
+
+- Issue #20143: The line numbers reported in Argument Clinic errors are
+ now more accurate.
+
+- Issue #20142: Py_buffer variables generated by Argument Clinic are now
+ initialized with a default value.
+
+Build
+-----
+
+- Issue #12837: Silence a tautological comparison warning on OS X under Clang in
+ socketmodule.c.
+
+What's New in Python 3.4.0 Beta 2?
+==================================
+
+Release date: 2014-01-05
+
+Core and Builtins
+-----------------
+
+- Issue #17432: Drop UCS2 from names of Unicode functions in python3.def.
+
+- Issue #19526: Exclude all new API from the stable ABI. Exceptions can be
+ made if a need is demonstrated.
+
+- Issue #19969: PyBytes_FromFormatV() now raises an OverflowError if "%c"
+ argument is not in range [0; 255].
+
+- Issue #19995: %c, %o, %x, and %X now issue a DeprecationWarning on non-integer
+ input; reworded docs to clarify that an integer type should define both __int__
+ and __index__.
+
+- Issue #19787: PyThread_set_key_value() now always set the value. In Python
+ 3.3, the function did nothing if the key already exists (if the current value
+ is a non-NULL pointer).
+
+- Issue #14432: Remove the thread state field from the frame structure. Fix a
+ crash when a generator is created in a C thread that is destroyed while the
+ generator is still used. The issue was that a generator contains a frame, and
+ the frame kept a reference to the Python state of the destroyed C thread. The
+ crash occurs when a trace function is setup.
+
+- Issue #19576: PyGILState_Ensure() now initializes threads. At startup, Python
+ has no concrete GIL. If PyGILState_Ensure() is called from a new thread for
+ the first time and PyEval_InitThreads() was not called yet, a GIL needs to be
+ created.
+
+- Issue #17576: Deprecation warning emitted now when __int__() or __index__()
+ return not int instance.
+
+- Issue #19932: Fix typo in import.h, missing whitespaces in function prototypes.
+
+- Issue #19736: Add module-level statvfs constants defined for GNU/glibc
+ based systems.
+
+- Issue #20097: Fix bad use of "self" in importlib's WindowsRegistryFinder.
+
+- Issue #19729: In str.format(), fix recursive expansion in format spec.
+
+- Issue #19638: Fix possible crash / undefined behaviour from huge (more than 2
+ billion characters) input strings in _Py_dg_strtod.
+
+Library
+-------
+
+- Issue #20154: Deadlock in asyncio.StreamReader.readexactly().
+
+- Issue #16113: Remove sha3 module again.
+
+- Issue #20111: pathlib.Path.with_suffix() now sanity checks the given suffix.
+
+- Fix breakage in TestSuite.countTestCases() introduced by issue #11798.
+
- Issue #20108: Avoid parameter name clash in inspect.getcallargs().
-- Issue #12692: Backport the fix for ResourceWarning in test_urllib2net. This
- also helps in closing the socket when Connection Close header is not sent.
+- Issue #19918: Fix PurePath.relative_to() under Windows.
- Issue #19422: Explicitly disallow non-SOCK_STREAM sockets in the ssl
module, rather than silently let them emit clear text data.
--- /dev/null
- #!/usr/bin/env python3
- #
+# Argument Clinic
+# Copyright 2012-2013 by Larry Hastings.
+# Licensed to the PSF under a contributor agreement.
+#
+
+import builtins
+import clinic
+from clinic import DSLParser
+import collections
+import inspect
+from test import support
+import unittest
+from unittest import TestCase
+
+class FakeConverter:
+ def __init__(self, name, args):
+ self.name = name
+ self.args = args
+
+
+class FakeConverterFactory:
+ def __init__(self, name):
+ self.name = name
+
+ def __call__(self, name, default, **kwargs):
+ return FakeConverter(self.name, kwargs)
+
+
+class FakeConvertersDict:
+ def __init__(self):
+ self.used_converters = {}
+
+ def get(self, name, default):
+ return self.used_converters.setdefault(name, FakeConverterFactory(name))
+
+class FakeClinic:
+ def __init__(self):
+ self.converters = FakeConvertersDict()
+ self.legacy_converters = FakeConvertersDict()
+ self.language = clinic.CLanguage()
+ self.filename = None
+ self.block_parser = clinic.BlockParser('', self.language)
+ self.modules = collections.OrderedDict()
+ clinic.clinic = self
+ self.name = "FakeClinic"
+
+ def is_directive(self, name):
+ return name == "module"
+
+ def directive(self, name, args):
+ self.called_directives[name] = args
+
+ _module_and_class = clinic.Clinic._module_and_class
+
+class ClinicWholeFileTest(TestCase):
+ def test_eol(self):
+ # regression test:
+ # clinic's block parser didn't recognize
+ # the "end line" for the block if it
+ # didn't end in "\n" (as in, the last)
+ # byte of the file was '/'.
+ # so it woudl spit out an end line for you.
+ # and since you really already had one,
+ # the last line of the block got corrupted.
+ c = clinic.Clinic(clinic.CLanguage())
+ raw = "/*[clinic]\nfoo\n[clinic]*/"
+ cooked = c.parse(raw).splitlines()
+ end_line = cooked[2].rstrip()
+ # this test is redundant, it's just here explicitly to catch
+ # the regression test so we don't forget what it looked like
+ self.assertNotEqual(end_line, "[clinic]*/[clinic]*/")
+ self.assertEqual(end_line, "[clinic]*/")
+
+
+
+class ClinicGroupPermuterTest(TestCase):
+ def _test(self, l, m, r, output):
+ computed = clinic.permute_optional_groups(l, m, r)
+ self.assertEqual(output, computed)
+
+ def test_range(self):
+ self._test([['start']], ['stop'], [['step']],
+ (
+ ('stop',),
+ ('start', 'stop',),
+ ('start', 'stop', 'step',),
+ ))
+
+ def test_add_window(self):
+ self._test([['x', 'y']], ['ch'], [['attr']],
+ (
+ ('ch',),
+ ('ch', 'attr'),
+ ('x', 'y', 'ch',),
+ ('x', 'y', 'ch', 'attr'),
+ ))
+
+ def test_ludicrous(self):
+ self._test([['a1', 'a2', 'a3'], ['b1', 'b2']], ['c1'], [['d1', 'd2'], ['e1', 'e2', 'e3']],
+ (
+ ('c1',),
+ ('b1', 'b2', 'c1'),
+ ('b1', 'b2', 'c1', 'd1', 'd2'),
+ ('a1', 'a2', 'a3', 'b1', 'b2', 'c1'),
+ ('a1', 'a2', 'a3', 'b1', 'b2', 'c1', 'd1', 'd2'),
+ ('a1', 'a2', 'a3', 'b1', 'b2', 'c1', 'd1', 'd2', 'e1', 'e2', 'e3'),
+ ))
+
+ def test_right_only(self):
+ self._test([], [], [['a'],['b'],['c']],
+ (
+ (),
+ ('a',),
+ ('a', 'b'),
+ ('a', 'b', 'c')
+ ))
+
+ def test_have_left_options_but_required_is_empty(self):
+ def fn():
+ clinic.permute_optional_groups(['a'], [], [])
+ self.assertRaises(AssertionError, fn)
+
+
+class ClinicLinearFormatTest(TestCase):
+ def _test(self, input, output, **kwargs):
+ computed = clinic.linear_format(input, **kwargs)
+ self.assertEqual(output, computed)
+
+ def test_empty_strings(self):
+ self._test('', '')
+
+ def test_solo_newline(self):
+ self._test('\n', '\n')
+
+ def test_no_substitution(self):
+ self._test("""
+ abc
+ """, """
+ abc
+ """)
+
+ def test_empty_substitution(self):
+ self._test("""
+ abc
+ {name}
+ def
+ """, """
+ abc
+ def
+ """, name='')
+
+ def test_single_line_substitution(self):
+ self._test("""
+ abc
+ {name}
+ def
+ """, """
+ abc
+ GARGLE
+ def
+ """, name='GARGLE')
+
+ def test_multiline_substitution(self):
+ self._test("""
+ abc
+ {name}
+ def
+ """, """
+ abc
+ bingle
+ bungle
+
+ def
+ """, name='bingle\nbungle\n')
+
+class InertParser:
+ def __init__(self, clinic):
+ pass
+
+ def parse(self, block):
+ pass
+
+class CopyParser:
+ def __init__(self, clinic):
+ pass
+
+ def parse(self, block):
+ block.output = block.input
+
+
+class ClinicBlockParserTest(TestCase):
+ def _test(self, input, output):
+ language = clinic.CLanguage()
+
+ blocks = list(clinic.BlockParser(input, language))
+ writer = clinic.BlockPrinter(language)
+ for block in blocks:
+ writer.print_block(block)
+ output = writer.f.getvalue()
+ assert output == input, "output != input!\n\noutput " + repr(output) + "\n\n input " + repr(input)
+
+ def round_trip(self, input):
+ return self._test(input, input)
+
+ def test_round_trip_1(self):
+ self.round_trip("""
+ verbatim text here
+ lah dee dah
+""")
+ def test_round_trip_2(self):
+ self.round_trip("""
+ verbatim text here
+ lah dee dah
+/*[inert]
+abc
+[inert]*/
+def
+/*[inert checksum: 7b18d017f89f61cf17d47f92749ea6930a3f1deb]*/
+xyz
+""")
+
+ def _test_clinic(self, input, output):
+ language = clinic.CLanguage()
+ c = clinic.Clinic(language)
+ c.parsers['inert'] = InertParser(c)
+ c.parsers['copy'] = CopyParser(c)
+ computed = c.parse(input)
+ self.assertEqual(output, computed)
+
+ def test_clinic_1(self):
+ self._test_clinic("""
+ verbatim text here
+ lah dee dah
+/*[copy]
+def
+[copy]*/
+abc
+/*[copy checksum: 03cfd743661f07975fa2f1220c5194cbaff48451]*/
+xyz
+""", """
+ verbatim text here
+ lah dee dah
+/*[copy]
+def
+[copy]*/
+def
+/*[copy checksum: 7b18d017f89f61cf17d47f92749ea6930a3f1deb]*/
+xyz
+""")
+
+
+class ClinicParserTest(TestCase):
+ def test_trivial(self):
+ parser = DSLParser(FakeClinic())
+ block = clinic.Block("module os\nos.access")
+ parser.parse(block)
+ module, function = block.signatures
+ self.assertEqual("access", function.name)
+ self.assertEqual("os", module.name)
+
+ def test_ignore_line(self):
+ block = self.parse("#\nmodule os\nos.access")
+ module, function = block.signatures
+ self.assertEqual("access", function.name)
+ self.assertEqual("os", module.name)
+
+ def test_param(self):
+ function = self.parse_function("module os\nos.access\n path: int")
+ self.assertEqual("access", function.name)
+ self.assertEqual(1, len(function.parameters))
+ p = function.parameters['path']
+ self.assertEqual('path', p.name)
+ self.assertIsInstance(p.converter, clinic.int_converter)
+
+ def test_param_default(self):
+ function = self.parse_function("module os\nos.access\n follow_symlinks: bool = True")
+ p = function.parameters['follow_symlinks']
+ self.assertEqual(True, p.default)
+
+ def test_param_no_docstring(self):
+ function = self.parse_function("""
+module os
+os.access
+ follow_symlinks: bool = True
+ something_else: str""")
+ p = function.parameters['follow_symlinks']
+ self.assertEqual(2, len(function.parameters))
+ self.assertIsInstance(function.parameters['something_else'].converter, clinic.str_converter)
+
+ def disabled_test_converter_arguments(self):
+ function = self.parse_function("module os\nos.access\n path: path_t(allow_fd=1)")
+ p = function.parameters['path']
+ self.assertEqual(1, p.converter.args['allow_fd'])
+
+ def test_param_docstring(self):
+ function = self.parse_function("""
+module os
+os.stat as os_stat_fn -> object(doc_default='stat_result')
+
+ path: str
+ Path to be examined""")
+ p = function.parameters['path']
+ self.assertEqual("Path to be examined", p.docstring)
+ self.assertEqual(function.return_converter.doc_default, 'stat_result')
+
+ def test_function_docstring(self):
+ function = self.parse_function("""
+module os
+os.stat as os_stat_fn
+
+ path: str
+ Path to be examined
+
+Perform a stat system call on the given path.""")
+ self.assertEqual("""
+stat(path)
+Perform a stat system call on the given path.
+
+ path
+ Path to be examined
+""".strip(), function.docstring)
+
+ def test_explicit_parameters_in_docstring(self):
+ function = self.parse_function("""
+module foo
+foo.bar
+ x: int
+ Documentation for x.
+ y: int
+
+This is the documentation for foo.
+
+Okay, we're done here.
+""")
+ self.assertEqual("""
+bar(x, y)
+This is the documentation for foo.
+
+ x
+ Documentation for x.
+
+Okay, we're done here.
+""".strip(), function.docstring)
+
+ def test_parser_regression_special_character_in_parameter_column_of_docstring_first_line(self):
+ function = self.parse_function("""
+module os
+os.stat
+ path: str
+This/used to break Clinic!
+""")
+ self.assertEqual("os.stat(path)\n\nThis/used to break Clinic!", function.docstring)
+
+ def test_c_name(self):
+ function = self.parse_function("module os\nos.stat as os_stat_fn")
+ self.assertEqual("os_stat_fn", function.c_basename)
+
+ def test_return_converter(self):
+ function = self.parse_function("module os\nos.stat -> int")
+ self.assertIsInstance(function.return_converter, clinic.int_return_converter)
+
+ def test_star(self):
+ function = self.parse_function("module os\nos.access\n *\n follow_symlinks: bool = True")
+ p = function.parameters['follow_symlinks']
+ self.assertEqual(inspect.Parameter.KEYWORD_ONLY, p.kind)
+ self.assertEqual(0, p.group)
+
+ def test_group(self):
+ function = self.parse_function("module window\nwindow.border\n [\n ls : int\n ]\n /\n")
+ p = function.parameters['ls']
+ self.assertEqual(1, p.group)
+
+ def test_left_group(self):
+ function = self.parse_function("""
+module curses
+curses.addch
+ [
+ y: int
+ Y-coordinate.
+ x: int
+ X-coordinate.
+ ]
+ ch: char
+ Character to add.
+ [
+ attr: long
+ Attributes for the character.
+ ]
+ /
+""")
+ for name, group in (
+ ('y', -1), ('x', -1),
+ ('ch', 0),
+ ('attr', 1),
+ ):
+ p = function.parameters[name]
+ self.assertEqual(p.group, group)
+ self.assertEqual(p.kind, inspect.Parameter.POSITIONAL_ONLY)
+ self.assertEqual(function.docstring.strip(), """
+addch([y, x,] ch, [attr])
+
+
+ y
+ Y-coordinate.
+ x
+ X-coordinate.
+ ch
+ Character to add.
+ attr
+ Attributes for the character.
+ """.strip())
+
+ def test_nested_groups(self):
+ function = self.parse_function("""
+module curses
+curses.imaginary
+ [
+ [
+ y1: int
+ Y-coordinate.
+ y2: int
+ Y-coordinate.
+ ]
+ x1: int
+ X-coordinate.
+ x2: int
+ X-coordinate.
+ ]
+ ch: char
+ Character to add.
+ [
+ attr1: long
+ Attributes for the character.
+ attr2: long
+ Attributes for the character.
+ attr3: long
+ Attributes for the character.
+ [
+ attr4: long
+ Attributes for the character.
+ attr5: long
+ Attributes for the character.
+ attr6: long
+ Attributes for the character.
+ ]
+ ]
+ /
+""")
+ for name, group in (
+ ('y1', -2), ('y2', -2),
+ ('x1', -1), ('x2', -1),
+ ('ch', 0),
+ ('attr1', 1), ('attr2', 1), ('attr3', 1),
+ ('attr4', 2), ('attr5', 2), ('attr6', 2),
+ ):
+ p = function.parameters[name]
+ self.assertEqual(p.group, group)
+ self.assertEqual(p.kind, inspect.Parameter.POSITIONAL_ONLY)
+
+ self.assertEqual(function.docstring.strip(), """
+imaginary([[y1, y2,] x1, x2,] ch, [attr1, attr2, attr3, [attr4, attr5, attr6]])
+
+
+ y1
+ Y-coordinate.
+ y2
+ Y-coordinate.
+ x1
+ X-coordinate.
+ x2
+ X-coordinate.
+ ch
+ Character to add.
+ attr1
+ Attributes for the character.
+ attr2
+ Attributes for the character.
+ attr3
+ Attributes for the character.
+ attr4
+ Attributes for the character.
+ attr5
+ Attributes for the character.
+ attr6
+ Attributes for the character.
+ """.strip())
+
+ def parse_function_should_fail(self, s):
+ with support.captured_stdout() as stdout:
+ with self.assertRaises(SystemExit):
+ self.parse_function(s)
+ return stdout.getvalue()
+
+ def test_disallowed_grouping__two_top_groups_on_left(self):
+ s = self.parse_function_should_fail("""
+module foo
+foo.two_top_groups_on_left
+ [
+ group1 : int
+ ]
+ [
+ group2 : int
+ ]
+ param: int
+ """)
+ self.assertEqual(s,
+ ('Error on line 0:\n'
+ 'Function two_top_groups_on_left has an unsupported group configuration. (Unexpected state 2)\n'))
+
+ def test_disallowed_grouping__two_top_groups_on_right(self):
+ self.parse_function_should_fail("""
+module foo
+foo.two_top_groups_on_right
+ param: int
+ [
+ group1 : int
+ ]
+ [
+ group2 : int
+ ]
+ """)
+
+ def test_disallowed_grouping__parameter_after_group_on_right(self):
+ self.parse_function_should_fail("""
+module foo
+foo.parameter_after_group_on_right
+ param: int
+ [
+ [
+ group1 : int
+ ]
+ group2 : int
+ ]
+ """)
+
+ def test_disallowed_grouping__group_after_parameter_on_left(self):
+ self.parse_function_should_fail("""
+module foo
+foo.group_after_parameter_on_left
+ [
+ group2 : int
+ [
+ group1 : int
+ ]
+ ]
+ param: int
+ """)
+
+ def test_disallowed_grouping__empty_group_on_left(self):
+ self.parse_function_should_fail("""
+module foo
+foo.empty_group
+ [
+ [
+ ]
+ group2 : int
+ ]
+ param: int
+ """)
+
+ def test_disallowed_grouping__empty_group_on_right(self):
+ self.parse_function_should_fail("""
+module foo
+foo.empty_group
+ param: int
+ [
+ [
+ ]
+ group2 : int
+ ]
+ """)
+
+ def test_no_parameters(self):
+ function = self.parse_function("""
+module foo
+foo.bar
+
+Docstring
+
+""")
+ self.assertEqual("bar()\nDocstring", function.docstring)
+ self.assertEqual(0, len(function.parameters))
+
+ def test_illegal_module_line(self):
+ self.parse_function_should_fail("""
+module foo
+foo.bar => int
+ /
+""")
+
+ def test_illegal_c_basename(self):
+ self.parse_function_should_fail("""
+module foo
+foo.bar as 935
+ /
+""")
+
+ def test_single_star(self):
+ self.parse_function_should_fail("""
+module foo
+foo.bar
+ *
+ *
+""")
+
+ def test_parameters_required_after_star_without_initial_parameters_or_docstring(self):
+ self.parse_function_should_fail("""
+module foo
+foo.bar
+ *
+""")
+
+ def test_parameters_required_after_star_without_initial_parameters_with_docstring(self):
+ self.parse_function_should_fail("""
+module foo
+foo.bar
+ *
+Docstring here.
+""")
+
+ def test_parameters_required_after_star_with_initial_parameters_without_docstring(self):
+ self.parse_function_should_fail("""
+module foo
+foo.bar
+ this: int
+ *
+""")
+
+ def test_parameters_required_after_star_with_initial_parameters_and_docstring(self):
+ self.parse_function_should_fail("""
+module foo
+foo.bar
+ this: int
+ *
+Docstring.
+""")
+
+ def test_single_slash(self):
+ self.parse_function_should_fail("""
+module foo
+foo.bar
+ /
+ /
+""")
+
+ def test_mix_star_and_slash(self):
+ self.parse_function_should_fail("""
+module foo
+foo.bar
+ x: int
+ y: int
+ *
+ z: int
+ /
+""")
+
+ def test_parameters_not_permitted_after_slash_for_now(self):
+ self.parse_function_should_fail("""
+module foo
+foo.bar
+ /
+ x: int
+""")
+
+ def test_function_not_at_column_0(self):
+ function = self.parse_function("""
+ module foo
+ foo.bar
+ x: int
+ Nested docstring here, goeth.
+ *
+ y: str
+ Not at column 0!
+""")
+ self.assertEqual("""
+bar(x, *, y)
+Not at column 0!
+
+ x
+ Nested docstring here, goeth.
+""".strip(), function.docstring)
+
+ def test_parser_regression_special_character_in_parameter_column_of_docstring_first_line(self):
+ function = self.parse_function("""
+module os
+os.stat
+ path: str
+This/used to break Clinic!
+""")
+ self.assertEqual("stat(path)\nThis/used to break Clinic!", function.docstring)
+
+ def test_directive(self):
+ c = FakeClinic()
+ parser = DSLParser(c)
+ parser.flag = False
+ parser.directives['setflag'] = lambda : setattr(parser, 'flag', True)
+ block = clinic.Block("setflag")
+ parser.parse(block)
+ self.assertTrue(parser.flag)
+
+ def test_legacy_converters(self):
+ block = self.parse('module os\nos.access\n path: "s"')
+ module, function = block.signatures
+ self.assertIsInstance((function.parameters['path']).converter, clinic.str_converter)
+
+ def parse(self, text):
+ c = FakeClinic()
+ parser = DSLParser(c)
+ block = clinic.Block(text)
+ parser.parse(block)
+ return block
+
+ def parse_function(self, text):
+ block = self.parse(text)
+ s = block.signatures
+ self.assertEqual(len(s), 2)
+ assert isinstance(s[0], clinic.Module)
+ assert isinstance(s[1], clinic.Function)
+ return s[1]
+
+ def test_scaffolding(self):
+ # test repr on special values
+ self.assertEqual(repr(clinic.unspecified), '<Unspecified>')
+ self.assertEqual(repr(clinic.NULL), '<Null>')
+
+ # test that fail fails
+ with support.captured_stdout() as stdout:
+ with self.assertRaises(SystemExit):
+ clinic.fail('The igloos are melting!', filename='clown.txt', line_number=69)
+ self.assertEqual(stdout.getvalue(), 'Error in file "clown.txt" on line 69:\nThe igloos are melting!\n')
+
+
+if __name__ == "__main__":
+ unittest.main()