Element-Detect-box-qr-code-packhouse1-inline3 / .venv /lib /python3.13 /site-packages /sympy /testing /tests /test_code_quality.py
| # coding=utf-8 | |
| from os import walk, sep, pardir | |
| from os.path import split, join, abspath, exists, isfile | |
| from glob import glob | |
| import re | |
| import random | |
| import ast | |
| from sympy.testing.pytest import raises | |
| from sympy.testing.quality_unicode import _test_this_file_encoding | |
| # System path separator (usually slash or backslash) to be | |
| # used with excluded files, e.g. | |
| # exclude = set([ | |
| # "%(sep)smpmath%(sep)s" % sepd, | |
| # ]) | |
| sepd = {"sep": sep} | |
| # path and sympy_path | |
| SYMPY_PATH = abspath(join(split(__file__)[0], pardir, pardir)) # go to sympy/ | |
| assert exists(SYMPY_PATH) | |
| TOP_PATH = abspath(join(SYMPY_PATH, pardir)) | |
| BIN_PATH = join(TOP_PATH, "bin") | |
| EXAMPLES_PATH = join(TOP_PATH, "examples") | |
| # Error messages | |
| message_space = "File contains trailing whitespace: %s, line %s." | |
| message_implicit = "File contains an implicit import: %s, line %s." | |
| message_tabs = "File contains tabs instead of spaces: %s, line %s." | |
| message_carriage = "File contains carriage returns at end of line: %s, line %s" | |
| message_str_raise = "File contains string exception: %s, line %s" | |
| message_gen_raise = "File contains generic exception: %s, line %s" | |
| message_old_raise = "File contains old-style raise statement: %s, line %s, \"%s\"" | |
| message_eof = "File does not end with a newline: %s, line %s" | |
| message_multi_eof = "File ends with more than 1 newline: %s, line %s" | |
| message_test_suite_def = "Function should start with 'test_' or '_': %s, line %s" | |
| message_duplicate_test = "This is a duplicate test function: %s, line %s" | |
| message_self_assignments = "File contains assignments to self/cls: %s, line %s." | |
| message_func_is = "File contains '.func is': %s, line %s." | |
| message_bare_expr = "File contains bare expression: %s, line %s." | |
| implicit_test_re = re.compile(r'^\s*(>>> )?(\.\.\. )?from .* import .*\*') | |
| str_raise_re = re.compile( | |
| r'^\s*(>>> )?(\.\.\. )?raise(\s+(\'|\")|\s*(\(\s*)+(\'|\"))') | |
| gen_raise_re = re.compile( | |
| r'^\s*(>>> )?(\.\.\. )?raise(\s+Exception|\s*(\(\s*)+Exception)') | |
| old_raise_re = re.compile(r'^\s*(>>> )?(\.\.\. )?raise((\s*\(\s*)|\s+)\w+\s*,') | |
| test_suite_def_re = re.compile(r'^def\s+(?!(_|test))[^(]*\(\s*\)\s*:$') | |
| test_ok_def_re = re.compile(r'^def\s+test_.*:$') | |
| test_file_re = re.compile(r'.*[/\\]test_.*\.py$') | |
| func_is_re = re.compile(r'\.\s*func\s+is') | |
| def tab_in_leading(s): | |
| """Returns True if there are tabs in the leading whitespace of a line, | |
| including the whitespace of docstring code samples.""" | |
| n = len(s) - len(s.lstrip()) | |
| if not s[n:n + 3] in ['...', '>>>']: | |
| check = s[:n] | |
| else: | |
| smore = s[n + 3:] | |
| check = s[:n] + smore[:len(smore) - len(smore.lstrip())] | |
| return not (check.expandtabs() == check) | |
| def find_self_assignments(s): | |
| """Returns a list of "bad" assignments: if there are instances | |
| of assigning to the first argument of the class method (except | |
| for staticmethod's). | |
| """ | |
| t = [n for n in ast.parse(s).body if isinstance(n, ast.ClassDef)] | |
| bad = [] | |
| for c in t: | |
| for n in c.body: | |
| if not isinstance(n, ast.FunctionDef): | |
| continue | |
| if any(d.id == 'staticmethod' | |
| for d in n.decorator_list if isinstance(d, ast.Name)): | |
| continue | |
| if n.name == '__new__': | |
| continue | |
| if not n.args.args: | |
| continue | |
| first_arg = n.args.args[0].arg | |
| for m in ast.walk(n): | |
| if isinstance(m, ast.Assign): | |
| for a in m.targets: | |
| if isinstance(a, ast.Name) and a.id == first_arg: | |
| bad.append(m) | |
| elif (isinstance(a, ast.Tuple) and | |
| any(q.id == first_arg for q in a.elts | |
| if isinstance(q, ast.Name))): | |
| bad.append(m) | |
| return bad | |
| def check_directory_tree(base_path, file_check, exclusions=set(), pattern="*.py"): | |
| """ | |
| Checks all files in the directory tree (with base_path as starting point) | |
| with the file_check function provided, skipping files that contain | |
| any of the strings in the set provided by exclusions. | |
| """ | |
| if not base_path: | |
| return | |
| for root, dirs, files in walk(base_path): | |
| check_files(glob(join(root, pattern)), file_check, exclusions) | |
| def check_files(files, file_check, exclusions=set(), pattern=None): | |
| """ | |
| Checks all files with the file_check function provided, skipping files | |
| that contain any of the strings in the set provided by exclusions. | |
| """ | |
| if not files: | |
| return | |
| for fname in files: | |
| if not exists(fname) or not isfile(fname): | |
| continue | |
| if any(ex in fname for ex in exclusions): | |
| continue | |
| if pattern is None or re.match(pattern, fname): | |
| file_check(fname) | |
| class _Visit(ast.NodeVisitor): | |
| """return the line number corresponding to the | |
| line on which a bare expression appears if it is a binary op | |
| or a comparison that is not in a with block. | |
| EXAMPLES | |
| ======== | |
| >>> import ast | |
| >>> class _Visit(ast.NodeVisitor): | |
| ... def visit_Expr(self, node): | |
| ... if isinstance(node.value, (ast.BinOp, ast.Compare)): | |
| ... print(node.lineno) | |
| ... def visit_With(self, node): | |
| ... pass # no checking there | |
| ... | |
| >>> code='''x = 1 # line 1 | |
| ... for i in range(3): | |
| ... x == 2 # <-- 3 | |
| ... if x == 2: | |
| ... x == 3 # <-- 5 | |
| ... x + 1 # <-- 6 | |
| ... x = 1 | |
| ... if x == 1: | |
| ... print(1) | |
| ... while x != 1: | |
| ... x == 1 # <-- 11 | |
| ... with raises(TypeError): | |
| ... c == 1 | |
| ... raise TypeError | |
| ... assert x == 1 | |
| ... ''' | |
| >>> _Visit().visit(ast.parse(code)) | |
| 3 | |
| 5 | |
| 6 | |
| 11 | |
| """ | |
| def visit_Expr(self, node): | |
| if isinstance(node.value, (ast.BinOp, ast.Compare)): | |
| assert None, message_bare_expr % ('', node.lineno) | |
| def visit_With(self, node): | |
| pass | |
| BareExpr = _Visit() | |
| def line_with_bare_expr(code): | |
| """return None or else 0-based line number of code on which | |
| a bare expression appeared. | |
| """ | |
| tree = ast.parse(code) | |
| try: | |
| BareExpr.visit(tree) | |
| except AssertionError as msg: | |
| assert msg.args | |
| msg = msg.args[0] | |
| assert msg.startswith(message_bare_expr.split(':', 1)[0]) | |
| return int(msg.rsplit(' ', 1)[1].rstrip('.')) # the line number | |
| def test_files(): | |
| """ | |
| This test tests all files in SymPy and checks that: | |
| o no lines contains a trailing whitespace | |
| o no lines end with \r\n | |
| o no line uses tabs instead of spaces | |
| o that the file ends with a single newline | |
| o there are no general or string exceptions | |
| o there are no old style raise statements | |
| o name of arg-less test suite functions start with _ or test_ | |
| o no duplicate function names that start with test_ | |
| o no assignments to self variable in class methods | |
| o no lines contain ".func is" except in the test suite | |
| o there is no do-nothing expression like `a == b` or `x + 1` | |
| """ | |
| def test(fname): | |
| with open(fname, encoding="utf8") as test_file: | |
| test_this_file(fname, test_file) | |
| with open(fname, encoding='utf8') as test_file: | |
| _test_this_file_encoding(fname, test_file) | |
| def test_this_file(fname, test_file): | |
| idx = None | |
| code = test_file.read() | |
| test_file.seek(0) # restore reader to head | |
| py = fname if sep not in fname else fname.rsplit(sep, 1)[-1] | |
| if py.startswith('test_'): | |
| idx = line_with_bare_expr(code) | |
| if idx is not None: | |
| assert False, message_bare_expr % (fname, idx + 1) | |
| line = None # to flag the case where there were no lines in file | |
| tests = 0 | |
| test_set = set() | |
| for idx, line in enumerate(test_file): | |
| if test_file_re.match(fname): | |
| if test_suite_def_re.match(line): | |
| assert False, message_test_suite_def % (fname, idx + 1) | |
| if test_ok_def_re.match(line): | |
| tests += 1 | |
| test_set.add(line[3:].split('(')[0].strip()) | |
| if len(test_set) != tests: | |
| assert False, message_duplicate_test % (fname, idx + 1) | |
| if line.endswith((" \n", "\t\n")): | |
| assert False, message_space % (fname, idx + 1) | |
| if line.endswith("\r\n"): | |
| assert False, message_carriage % (fname, idx + 1) | |
| if tab_in_leading(line): | |
| assert False, message_tabs % (fname, idx + 1) | |
| if str_raise_re.search(line): | |
| assert False, message_str_raise % (fname, idx + 1) | |
| if gen_raise_re.search(line): | |
| assert False, message_gen_raise % (fname, idx + 1) | |
| if (implicit_test_re.search(line) and | |
| not list(filter(lambda ex: ex in fname, import_exclude))): | |
| assert False, message_implicit % (fname, idx + 1) | |
| if func_is_re.search(line) and not test_file_re.search(fname): | |
| assert False, message_func_is % (fname, idx + 1) | |
| result = old_raise_re.search(line) | |
| if result is not None: | |
| assert False, message_old_raise % ( | |
| fname, idx + 1, result.group(2)) | |
| if line is not None: | |
| if line == '\n' and idx > 0: | |
| assert False, message_multi_eof % (fname, idx + 1) | |
| elif not line.endswith('\n'): | |
| # eof newline check | |
| assert False, message_eof % (fname, idx + 1) | |
| # Files to test at top level | |
| top_level_files = [join(TOP_PATH, file) for file in [ | |
| "isympy.py", | |
| "build.py", | |
| "setup.py", | |
| ]] | |
| # Files to exclude from all tests | |
| exclude = { | |
| "%(sep)ssympy%(sep)sparsing%(sep)sautolev%(sep)s_antlr%(sep)sautolevparser.py" % sepd, | |
| "%(sep)ssympy%(sep)sparsing%(sep)sautolev%(sep)s_antlr%(sep)sautolevlexer.py" % sepd, | |
| "%(sep)ssympy%(sep)sparsing%(sep)sautolev%(sep)s_antlr%(sep)sautolevlistener.py" % sepd, | |
| "%(sep)ssympy%(sep)sparsing%(sep)slatex%(sep)s_antlr%(sep)slatexparser.py" % sepd, | |
| "%(sep)ssympy%(sep)sparsing%(sep)slatex%(sep)s_antlr%(sep)slatexlexer.py" % sepd, | |
| } | |
| # Files to exclude from the implicit import test | |
| import_exclude = { | |
| # glob imports are allowed in top-level __init__.py: | |
| "%(sep)ssympy%(sep)s__init__.py" % sepd, | |
| # these __init__.py should be fixed: | |
| # XXX: not really, they use useful import pattern (DRY) | |
| "%(sep)svector%(sep)s__init__.py" % sepd, | |
| "%(sep)smechanics%(sep)s__init__.py" % sepd, | |
| "%(sep)squantum%(sep)s__init__.py" % sepd, | |
| "%(sep)spolys%(sep)s__init__.py" % sepd, | |
| "%(sep)spolys%(sep)sdomains%(sep)s__init__.py" % sepd, | |
| # interactive SymPy executes ``from sympy import *``: | |
| "%(sep)sinteractive%(sep)ssession.py" % sepd, | |
| # isympy.py executes ``from sympy import *``: | |
| "%(sep)sisympy.py" % sepd, | |
| # these two are import timing tests: | |
| "%(sep)sbin%(sep)ssympy_time.py" % sepd, | |
| "%(sep)sbin%(sep)ssympy_time_cache.py" % sepd, | |
| # Taken from Python stdlib: | |
| "%(sep)sparsing%(sep)ssympy_tokenize.py" % sepd, | |
| # this one should be fixed: | |
| "%(sep)splotting%(sep)spygletplot%(sep)s" % sepd, | |
| # False positive in the docstring | |
| "%(sep)sbin%(sep)stest_external_imports.py" % sepd, | |
| "%(sep)sbin%(sep)stest_submodule_imports.py" % sepd, | |
| # These are deprecated stubs that can be removed at some point: | |
| "%(sep)sutilities%(sep)sruntests.py" % sepd, | |
| "%(sep)sutilities%(sep)spytest.py" % sepd, | |
| "%(sep)sutilities%(sep)srandtest.py" % sepd, | |
| "%(sep)sutilities%(sep)stmpfiles.py" % sepd, | |
| "%(sep)sutilities%(sep)squality_unicode.py" % sepd, | |
| } | |
| check_files(top_level_files, test) | |
| check_directory_tree(BIN_PATH, test, {"~", ".pyc", ".sh"}, "*") | |
| check_directory_tree(SYMPY_PATH, test, exclude) | |
| check_directory_tree(EXAMPLES_PATH, test, exclude) | |
| def _with_space(c): | |
| # return c with a random amount of leading space | |
| return random.randint(0, 10)*' ' + c | |
| def test_raise_statement_regular_expression(): | |
| candidates_ok = [ | |
| "some text # raise Exception, 'text'", | |
| "raise ValueError('text') # raise Exception, 'text'", | |
| "raise ValueError('text')", | |
| "raise ValueError", | |
| "raise ValueError('text')", | |
| "raise ValueError('text') #,", | |
| # Talking about an exception in a docstring | |
| ''''"""This function will raise ValueError, except when it doesn't"""''', | |
| "raise (ValueError('text')", | |
| ] | |
| str_candidates_fail = [ | |
| "raise 'exception'", | |
| "raise 'Exception'", | |
| 'raise "exception"', | |
| 'raise "Exception"', | |
| "raise 'ValueError'", | |
| ] | |
| gen_candidates_fail = [ | |
| "raise Exception('text') # raise Exception, 'text'", | |
| "raise Exception('text')", | |
| "raise Exception", | |
| "raise Exception('text')", | |
| "raise Exception('text') #,", | |
| "raise Exception, 'text'", | |
| "raise Exception, 'text' # raise Exception('text')", | |
| "raise Exception, 'text' # raise Exception, 'text'", | |
| ">>> raise Exception, 'text'", | |
| ">>> raise Exception, 'text' # raise Exception('text')", | |
| ">>> raise Exception, 'text' # raise Exception, 'text'", | |
| ] | |
| old_candidates_fail = [ | |
| "raise Exception, 'text'", | |
| "raise Exception, 'text' # raise Exception('text')", | |
| "raise Exception, 'text' # raise Exception, 'text'", | |
| ">>> raise Exception, 'text'", | |
| ">>> raise Exception, 'text' # raise Exception('text')", | |
| ">>> raise Exception, 'text' # raise Exception, 'text'", | |
| "raise ValueError, 'text'", | |
| "raise ValueError, 'text' # raise Exception('text')", | |
| "raise ValueError, 'text' # raise Exception, 'text'", | |
| ">>> raise ValueError, 'text'", | |
| ">>> raise ValueError, 'text' # raise Exception('text')", | |
| ">>> raise ValueError, 'text' # raise Exception, 'text'", | |
| "raise(ValueError,", | |
| "raise (ValueError,", | |
| "raise( ValueError,", | |
| "raise ( ValueError,", | |
| "raise(ValueError ,", | |
| "raise (ValueError ,", | |
| "raise( ValueError ,", | |
| "raise ( ValueError ,", | |
| ] | |
| for c in candidates_ok: | |
| assert str_raise_re.search(_with_space(c)) is None, c | |
| assert gen_raise_re.search(_with_space(c)) is None, c | |
| assert old_raise_re.search(_with_space(c)) is None, c | |
| for c in str_candidates_fail: | |
| assert str_raise_re.search(_with_space(c)) is not None, c | |
| for c in gen_candidates_fail: | |
| assert gen_raise_re.search(_with_space(c)) is not None, c | |
| for c in old_candidates_fail: | |
| assert old_raise_re.search(_with_space(c)) is not None, c | |
| def test_implicit_imports_regular_expression(): | |
| candidates_ok = [ | |
| "from sympy import something", | |
| ">>> from sympy import something", | |
| "from sympy.somewhere import something", | |
| ">>> from sympy.somewhere import something", | |
| "import sympy", | |
| ">>> import sympy", | |
| "import sympy.something.something", | |
| "... import sympy", | |
| "... import sympy.something.something", | |
| "... from sympy import something", | |
| "... from sympy.somewhere import something", | |
| ">> from sympy import *", # To allow 'fake' docstrings | |
| "# from sympy import *", | |
| "some text # from sympy import *", | |
| ] | |
| candidates_fail = [ | |
| "from sympy import *", | |
| ">>> from sympy import *", | |
| "from sympy.somewhere import *", | |
| ">>> from sympy.somewhere import *", | |
| "... from sympy import *", | |
| "... from sympy.somewhere import *", | |
| ] | |
| for c in candidates_ok: | |
| assert implicit_test_re.search(_with_space(c)) is None, c | |
| for c in candidates_fail: | |
| assert implicit_test_re.search(_with_space(c)) is not None, c | |
| def test_test_suite_defs(): | |
| candidates_ok = [ | |
| " def foo():\n", | |
| "def foo(arg):\n", | |
| "def _foo():\n", | |
| "def test_foo():\n", | |
| ] | |
| candidates_fail = [ | |
| "def foo():\n", | |
| "def foo() :\n", | |
| "def foo( ):\n", | |
| "def foo():\n", | |
| ] | |
| for c in candidates_ok: | |
| assert test_suite_def_re.search(c) is None, c | |
| for c in candidates_fail: | |
| assert test_suite_def_re.search(c) is not None, c | |
| def test_test_duplicate_defs(): | |
| candidates_ok = [ | |
| "def foo():\ndef foo():\n", | |
| "def test():\ndef test_():\n", | |
| "def test_():\ndef test__():\n", | |
| ] | |
| candidates_fail = [ | |
| "def test_():\ndef test_ ():\n", | |
| "def test_1():\ndef test_1():\n", | |
| ] | |
| ok = (None, 'check') | |
| def check(file): | |
| tests = 0 | |
| test_set = set() | |
| for idx, line in enumerate(file.splitlines()): | |
| if test_ok_def_re.match(line): | |
| tests += 1 | |
| test_set.add(line[3:].split('(')[0].strip()) | |
| if len(test_set) != tests: | |
| return False, message_duplicate_test % ('check', idx + 1) | |
| return None, 'check' | |
| for c in candidates_ok: | |
| assert check(c) == ok | |
| for c in candidates_fail: | |
| assert check(c) != ok | |
| def test_find_self_assignments(): | |
| candidates_ok = [ | |
| "class A(object):\n def foo(self, arg): arg = self\n", | |
| "class A(object):\n def foo(self, arg): self.prop = arg\n", | |
| "class A(object):\n def foo(self, arg): obj, obj2 = arg, self\n", | |
| "class A(object):\n @classmethod\n def bar(cls, arg): arg = cls\n", | |
| "class A(object):\n def foo(var, arg): arg = var\n", | |
| ] | |
| candidates_fail = [ | |
| "class A(object):\n def foo(self, arg): self = arg\n", | |
| "class A(object):\n def foo(self, arg): obj, self = arg, arg\n", | |
| "class A(object):\n def foo(self, arg):\n if arg: self = arg", | |
| "class A(object):\n @classmethod\n def foo(cls, arg): cls = arg\n", | |
| "class A(object):\n def foo(var, arg): var = arg\n", | |
| ] | |
| for c in candidates_ok: | |
| assert find_self_assignments(c) == [] | |
| for c in candidates_fail: | |
| assert find_self_assignments(c) != [] | |
| def test_test_unicode_encoding(): | |
| unicode_whitelist = ['foo'] | |
| unicode_strict_whitelist = ['bar'] | |
| fname = 'abc' | |
| test_file = ['α'] | |
| raises(AssertionError, lambda: _test_this_file_encoding( | |
| fname, test_file, unicode_whitelist, unicode_strict_whitelist)) | |
| fname = 'abc' | |
| test_file = ['abc'] | |
| _test_this_file_encoding( | |
| fname, test_file, unicode_whitelist, unicode_strict_whitelist) | |
| fname = 'foo' | |
| test_file = ['abc'] | |
| raises(AssertionError, lambda: _test_this_file_encoding( | |
| fname, test_file, unicode_whitelist, unicode_strict_whitelist)) | |
| fname = 'bar' | |
| test_file = ['abc'] | |
| _test_this_file_encoding( | |
| fname, test_file, unicode_whitelist, unicode_strict_whitelist) | |