hassle.generate_tests API documentation

# hassle.generate_tests

View Source

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

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

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

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

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

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

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

    100def generate_test_files(package_path: Pathier, tests_dir: Pathier = None):
    101    """Generate test files for all .py files in 'src'
    102    directory of 'package_path'."""
    103    pyfiles = [
    104        file
    105        for file in (package_path / "src").rglob("*.py")
    106        if file.name != "__init__.py"
    107    ]
    108    for pyfile in pyfiles:
    109        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

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