hassle.generate_tests API documentation

# hassle.generate_tests

View Source

      1import argparse
      2import os
      3import tokenize
      4
      5from pathier import Pathier
      6
      7root = Pathier(__file__).parent
      8
      9
     10def get_args() -> argparse.Namespace:
     11    parser = argparse.ArgumentParser()
     12
     13    parser.add_argument(
     14        "paths",
     15        type=str,
     16        default=".",
     17        nargs="*",
     18        help=""" The name of the package or project to generate tests for,
     19        assuming it's a subfolder of your current working directory.
     20        Can also be a full path to the package. If nothing is given,
     21        the current working directory will be used.
     22        Can also be individual files.""",
     23    )
     24
     25    parser.add_argument(
     26        "-t",
     27        "--tests_dir",
     28        type=str,
     29        default=None,
     30        help=""" A specific tests directory path to write tests to.
     31        When supplying individual files to paths arg, the default
     32        behavior is to create a 'tests' directory in the parent
     33        directory of the specified file, resulting in multiple
     34        'tests' directories being created if the files exist in
     35        subdirectories. Supply a path to this arg to override
     36        this behavior.""",
     37    )
     38
     39    args = parser.parse_args()
     40    return args
     41
     42
     43def get_function_names(filepath: Pathier) -> list[str]:
     44    """Returns a list of function names from a .py file."""
     45    with filepath.open("r") as file:
     46        tokens = list(tokenize.generate_tokens(file.readline))
     47    functions = []
     48    for i, token in enumerate(tokens):
     49        # If token.type is "name" and the preceeding token is "def"
     50        if (
     51            token.type == 1
     52            and tokens[i - 1].type == 1
     53            and tokens[i - 1].string == "def"
     54        ):
     55            functions.append(token.string)
     56    return functions
     57
     58
     59def write_placeholders(
     60    package_path: Pathier,
     61    pyfile: Pathier | str,
     62    functions: list[str],
     63    tests_dir: Pathier = None,
     64):
     65    """Write placeholder functions to the
     66    tests/test_{pyfile} file if they don't already exist.
     67    The placeholder functions use the naming convention
     68    test_{function_name}
     69
     70    :param package_path: Path to the package.
     71
     72    :param pyfile: Path to the pyfile to write placeholders for.
     73
     74    :param functions: List of functions to generate
     75    placehodlers for."""
     76    package_name = package_path.stem
     77    if not tests_dir:
     78        tests_dir = package_path / "tests"
     79    tests_dir.mkdir()
     80    pyfile = Pathier(pyfile)
     81    test_file = tests_dir / f"test_{pyfile.name}"
     82    # Makes sure not to overwrite previously written tests
     83    # or additional imports.
     84    if test_file.exists():
     85        content = test_file.read_text() + "\n\n"
     86    else:
     87        content = f"import pytest\nfrom {package_name} import {pyfile.stem}\n\n\n"
     88    for function in functions:
     89        test_function = f"def test_{function}"
     90        if test_function not in content and function != "__init__":
     91            content += f"{test_function}():\n    ...\n\n\n"
     92    test_file.write_text(content)
     93    os.system(f"black {tests_dir}")
     94    os.system(f"isort {tests_dir}")
     95
     96
     97def generate_test_files(package_path: Pathier, tests_dir: Pathier = None):
     98    """Generate test files for all .py files in 'src'
     99    directory of 'package_path'."""
    100    pyfiles = [
    101        file
    102        for file in (package_path / "src").rglob("*.py")
    103        if file.name != "__init__.py"
    104    ]
    105    for pyfile in pyfiles:
    106        write_placeholders(package_path, pyfile, get_function_names(pyfile), tests_dir)
    107
    108
    109def main(args: argparse.Namespace = None):
    110    if not args:
    111        args = get_args()
    112    args.paths = [Pathier(path).resolve() for path in args.paths]
    113    if args.tests_dir:
    114        args.tests_dir = Pathier(args.tests_dir).resolve()
    115    for path in args.paths:
    116        if path.is_dir():
    117            generate_test_files(path, args.tests_dir)
    118        elif path.is_file():
    119            write_placeholders(
    120                path.parent, path, get_function_names(path), args.tests_dir
    121            )
    122
    123
    124if __name__ == "__main__":
    125    main(get_args())

def get_args() -> argparse.Namespace: View Source

    11def get_args() -> argparse.Namespace:
    12    parser = argparse.ArgumentParser()
    13
    14    parser.add_argument(
    15        "paths",
    16        type=str,
    17        default=".",
    18        nargs="*",
    19        help=""" The name of the package or project to generate tests for,
    20        assuming it's a subfolder of your current working directory.
    21        Can also be a full path to the package. If nothing is given,
    22        the current working directory will be used.
    23        Can also be individual files.""",
    24    )
    25
    26    parser.add_argument(
    27        "-t",
    28        "--tests_dir",
    29        type=str,
    30        default=None,
    31        help=""" A specific tests directory path to write tests to.
    32        When supplying individual files to paths arg, the default
    33        behavior is to create a 'tests' directory in the parent
    34        directory of the specified file, resulting in multiple
    35        'tests' directories being created if the files exist in
    36        subdirectories. Supply a path to this arg to override
    37        this behavior.""",
    38    )
    39
    40    args = parser.parse_args()
    41    return args

def get_function_names(filepath: pathier.pathier.Pathier) -> list[str]: View Source

    44def get_function_names(filepath: Pathier) -> list[str]:
    45    """Returns a list of function names from a .py file."""
    46    with filepath.open("r") as file:
    47        tokens = list(tokenize.generate_tokens(file.readline))
    48    functions = []
    49    for i, token in enumerate(tokens):
    50        # If token.type is "name" and the preceeding token is "def"
    51        if (
    52            token.type == 1
    53            and tokens[i - 1].type == 1
    54            and tokens[i - 1].string == "def"
    55        ):
    56            functions.append(token.string)
    57    return functions

Returns a list of function names from a .py file.

def write_placeholders( package_path: pathier.pathier.Pathier, pyfile: pathier.pathier.Pathier | str, functions: list[str], tests_dir: pathier.pathier.Pathier = None): View Source

    60def write_placeholders(
    61    package_path: Pathier,
    62    pyfile: Pathier | str,
    63    functions: list[str],
    64    tests_dir: Pathier = None,
    65):
    66    """Write placeholder functions to the
    67    tests/test_{pyfile} file if they don't already exist.
    68    The placeholder functions use the naming convention
    69    test_{function_name}
    70
    71    :param package_path: Path to the package.
    72
    73    :param pyfile: Path to the pyfile to write placeholders for.
    74
    75    :param functions: List of functions to generate
    76    placehodlers for."""
    77    package_name = package_path.stem
    78    if not tests_dir:
    79        tests_dir = package_path / "tests"
    80    tests_dir.mkdir()
    81    pyfile = Pathier(pyfile)
    82    test_file = tests_dir / f"test_{pyfile.name}"
    83    # Makes sure not to overwrite previously written tests
    84    # or additional imports.
    85    if test_file.exists():
    86        content = test_file.read_text() + "\n\n"
    87    else:
    88        content = f"import pytest\nfrom {package_name} import {pyfile.stem}\n\n\n"
    89    for function in functions:
    90        test_function = f"def test_{function}"
    91        if test_function not in content and function != "__init__":
    92            content += f"{test_function}():\n    ...\n\n\n"
    93    test_file.write_text(content)
    94    os.system(f"black {tests_dir}")
    95    os.system(f"isort {tests_dir}")

Write placeholder functions to the tests/test_{pyfile} file if they don't already exist. The placeholder functions use the naming convention test_{function_name}

###### Parameters

- package_path: Path to the package.

- pyfile: Path to the pyfile to write placeholders for.

- functions: List of functions to generate placehodlers for.

def generate_test_files( package_path: pathier.pathier.Pathier, tests_dir: pathier.pathier.Pathier = None): View Source

     98def generate_test_files(package_path: Pathier, tests_dir: Pathier = None):
     99    """Generate test files for all .py files in 'src'
    100    directory of 'package_path'."""
    101    pyfiles = [
    102        file
    103        for file in (package_path / "src").rglob("*.py")
    104        if file.name != "__init__.py"
    105    ]
    106    for pyfile in pyfiles:
    107        write_placeholders(package_path, pyfile, get_function_names(pyfile), tests_dir)

Generate test files for all .py files in 'src' directory of 'package_path'.

def main(args: argparse.Namespace = None): View Source

    110def main(args: argparse.Namespace = None):
    111    if not args:
    112        args = get_args()
    113    args.paths = [Pathier(path).resolve() for path in args.paths]
    114    if args.tests_dir:
    115        args.tests_dir = Pathier(args.tests_dir).resolve()
    116    for path in args.paths:
    117        if path.is_dir():
    118            generate_test_files(path, args.tests_dir)
    119        elif path.is_file():
    120            write_placeholders(
    121                path.parent, path, get_function_names(path), args.tests_dir
    122            )