hassle.hassle API documentation

# hassle.hassle

View Source

      1import argparse
      2import os
      3import sys
      4
      5import isort
      6from gitbetter import Git
      7from pathier import Pathier
      8
      9from hassle import hassle_utilities
     10from hassle.generate_tests import generate_test_files
     11from hassle.run_tests import run_tests
     12
     13root = Pathier(__file__).parent
     14
     15
     16def get_args() -> argparse.Namespace:
     17    parser = argparse.ArgumentParser()
     18
     19    parser.add_argument(
     20        "package",
     21        type=str,
     22        default=".",
     23        nargs="?",
     24        help=""" The name of the package or project to use,
     25        assuming it's a subfolder of your current working directory.
     26        Can also be a full path to the package. If nothing is given,
     27        the current working directory will be used.""",
     28    )
     29
     30    parser.add_argument(
     31        "-b", "--build", action="store_true", help=""" Build the package. """
     32    )
     33
     34    parser.add_argument(
     35        "-t",
     36        "--tag_version",
     37        action="store_true",
     38        help=""" Add a git tag corresponding to the version in pyproject.toml. """,
     39    )
     40
     41    parser.add_argument(
     42        "-i",
     43        "--install",
     44        action="store_true",
     45        help=""" Install the package from source. """,
     46    )
     47
     48    parser.add_argument(
     49        "-iv",
     50        "--increment_version",
     51        type=str,
     52        default=None,
     53        choices=["major", "minor", "patch"],
     54        help=""" Increment version in pyproject.toml.
     55        Can be one of "major", "minor", or "patch". """,
     56    )
     57
     58    parser.add_argument(
     59        "-p",
     60        "--publish",
     61        action="store_true",
     62        help=""" Publish package to PyPi.
     63        Note: You must have configured twine
     64        and registered a PyPi account/generated an API
     65        key to use this option.""",
     66    )
     67
     68    parser.add_argument(
     69        "-rt",
     70        "--run_tests",
     71        action="store_true",
     72        help=""" Run tests for the package. """,
     73    )
     74
     75    parser.add_argument(
     76        "-gt",
     77        "--generate_tests",
     78        action="store_true",
     79        help=""" Generate tests for the package. """,
     80    )
     81
     82    parser.add_argument(
     83        "-uc",
     84        "--update_changelog",
     85        action="store_true",
     86        help=""" Update changelog file. """,
     87    )
     88
     89    parser.add_argument(
     90        "-od",
     91        "--overwrite_dependencies",
     92        action="store_true",
     93        help=""" When building a package, packagelister will be used
     94        to update the dependencies list in pyproject.toml.
     95        The default behavior is to append any new dependencies to
     96        the current list so as not to erase any manually added dependencies
     97        that packagelister may not detect. If you don't have any manually
     98        added dependencies and want to remove any dependencies that your
     99        project no longer uses, pass this flag.""",
    100    )
    101
    102    parser.add_argument(
    103        "-ca",
    104        "--commit_all",
    105        type=str,
    106        default=None,
    107        help=""" Git stage and commit all tracked files with this supplied commit message.
    108        If 'build' is passed, all commits will have message: 'chore: build v{current_version}""",
    109    )
    110
    111    parser.add_argument(
    112        "-s",
    113        "--sync",
    114        action="store_true",
    115        help=""" Pull from github, then push current commit to repo. """,
    116    )
    117
    118    parser.add_argument(
    119        "-dv",
    120        "--dependency_versions",
    121        action="store_true",
    122        help=""" Include version specifiers for dependencies in
    123        pyproject.toml.""",
    124    )
    125
    126    parser.add_argument(
    127        "-up",
    128        "--update",
    129        type=str,
    130        default=None,
    131        choices=["major", "minor", "patch"],
    132        help=""" Excpects one argument: "major", "minor", or "patch".
    133        Passing "-up minor" is equivalent to passing "--build --tag_version --increment_version minor --update_changelog --commit_all build --sync".
    134        To publish the updated package, the -p/--publish switch needs to be added to the cli input.
    135        To install the updated package, the -i/--install switch also needs to be added.""",
    136    )
    137
    138    parser.add_argument(
    139        "-st",
    140        "--skip_tests",
    141        action="store_true",
    142        help=""" Don't run tests when using the -b/--build command. """,
    143    )
    144
    145    args = parser.parse_args()
    146
    147    args.package = Pathier(args.package).resolve()
    148
    149    if args.update:
    150        args.build = True
    151        args.tag_version = True
    152        args.increment_version = args.update
    153        args.update_changelog = True
    154        args.commit_all = "build"
    155        args.sync = True
    156
    157    if args.increment_version and args.increment_version not in [
    158        "major",
    159        "minor",
    160        "patch",
    161    ]:
    162        raise ValueError(
    163            f"Invalid option for -iv/--increment_version: {args.increment_version}"
    164        )
    165
    166    if args.commit_all == "":
    167        raise ValueError("Commit message for args.commit_all cannot be empty.")
    168
    169    if args.publish and not hassle_utilities.on_primary_branch():
    170        print(
    171            "WARNING: You are trying to publish a project that does not appear to be on its main branch."
    172        )
    173        choice = input("Continue? (y/n) ")
    174        if choice != "y":
    175            print("Quitting hassle.")
    176            sys.exit()
    177
    178    return args
    179
    180
    181def build(
    182    package_dir: Pathier,
    183    skip_tests: bool = False,
    184    overwrite_dependencies: bool = False,
    185    increment_version: str | None = None,
    186):
    187    """Perform the build process.
    188
    189    Steps:
    190    * Run tests (unless `skip_tests` is `True`)
    191    * Raise error and abandon build if tests fail
    192    * Format source code with `Black`
    193    * Sort source code imports with `isort`
    194    * Update project dependencies in `pyproject.toml`
    195    * Increment version in `pyproject.toml` if `increment_version` supplied
    196    * Generate docs
    197    * Delete previous `dist` folder contents
    198    * Invoke build module"""
    199    if not skip_tests and not run_tests(package_dir):
    200        raise RuntimeError(
    201            f"ERROR: {package_dir.stem} failed testing.\nAbandoning build."
    202        )
    203    hassle_utilities.format_files(package_dir)
    204    [isort.file(path) for path in package_dir.rglob("*.py")]
    205    hassle_utilities.update_dependencies(
    206        package_dir / "pyproject.toml", overwrite_dependencies
    207    )
    208    if increment_version:
    209        hassle_utilities.increment_version(
    210            package_dir / "pyproject.toml", increment_version
    211        )
    212    # Vermin isn't taking into account the minimum version of dependencies.
    213    # Removing from now and defaulting to >=3.10
    214    # hassle_utilities.update_minimum_python_version(pyproject_path)
    215    hassle_utilities.generate_docs(package_dir)
    216    (package_dir / "dist").delete()
    217    os.system(f"{sys.executable} -m build {package_dir}")
    218
    219
    220def main(args: argparse.Namespace = None):
    221    if not args:
    222        args = get_args()
    223
    224    pyproject_path = args.package / "pyproject.toml"
    225    args.package.mkcwd()
    226
    227    git = Git()
    228
    229    if not pyproject_path.exists():
    230        raise FileNotFoundError(f"Could not locate pyproject.toml for {args.package}")
    231
    232    if args.generate_tests:
    233        generate_test_files(args.package)
    234
    235    if args.run_tests:
    236        run_tests(args.package)
    237
    238    if args.build:
    239        build(
    240            args.package,
    241            args.skip_tests,
    242            args.overwrite_dependencies,
    243            args.increment_version,
    244        )
    245
    246    if args.increment_version and not args.build:
    247        hassle_utilities.increment_version(pyproject_path, args.increment_version)
    248
    249    if args.commit_all:
    250        if args.commit_all == "build":
    251            version = pyproject_path.loads()["project"]["version"]
    252            args.commit_all = f"chore: build v{version}"
    253        git.add()
    254        git.commit(f'-m "{args.commit_all}"')
    255
    256    if args.tag_version:
    257        hassle_utilities.tag_version(args.package)
    258
    259    if args.update_changelog:
    260        hassle_utilities.update_changelog(pyproject_path)
    261        if args.tag_version:
    262            with git.capture_output():
    263                tags = git.tag("--sort=-committerdate")
    264                most_recent_tag = tags[: tags.find("\n")]
    265                git.execute(f"tag -d {most_recent_tag}")
    266        input("Press enter to continue after manually adjusting the changelog...")
    267        git.commit_files(
    268            [str(args.package / "CHANGELOG.md")], "chore: update changelog"
    269        )
    270        if args.tag_version:
    271            with git.capture_output():
    272                git.tag(most_recent_tag)
    273
    274    if args.publish:
    275        os.system(f"twine upload {args.package / 'dist' / '*'}")
    276
    277    if args.install:
    278        os.system(
    279            f"{sys.executable} -m pip install {args.package} --no-deps --upgrade --no-cache-dir"
    280        )
    281
    282    if args.sync:
    283        git.pull("--tags")
    284        git.push("--tags")
    285
    286
    287if __name__ == "__main__":
    288    main(get_args())

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

     17def get_args() -> argparse.Namespace:
     18    parser = argparse.ArgumentParser()
     19
     20    parser.add_argument(
     21        "package",
     22        type=str,
     23        default=".",
     24        nargs="?",
     25        help=""" The name of the package or project to use,
     26        assuming it's a subfolder of your current working directory.
     27        Can also be a full path to the package. If nothing is given,
     28        the current working directory will be used.""",
     29    )
     30
     31    parser.add_argument(
     32        "-b", "--build", action="store_true", help=""" Build the package. """
     33    )
     34
     35    parser.add_argument(
     36        "-t",
     37        "--tag_version",
     38        action="store_true",
     39        help=""" Add a git tag corresponding to the version in pyproject.toml. """,
     40    )
     41
     42    parser.add_argument(
     43        "-i",
     44        "--install",
     45        action="store_true",
     46        help=""" Install the package from source. """,
     47    )
     48
     49    parser.add_argument(
     50        "-iv",
     51        "--increment_version",
     52        type=str,
     53        default=None,
     54        choices=["major", "minor", "patch"],
     55        help=""" Increment version in pyproject.toml.
     56        Can be one of "major", "minor", or "patch". """,
     57    )
     58
     59    parser.add_argument(
     60        "-p",
     61        "--publish",
     62        action="store_true",
     63        help=""" Publish package to PyPi.
     64        Note: You must have configured twine
     65        and registered a PyPi account/generated an API
     66        key to use this option.""",
     67    )
     68
     69    parser.add_argument(
     70        "-rt",
     71        "--run_tests",
     72        action="store_true",
     73        help=""" Run tests for the package. """,
     74    )
     75
     76    parser.add_argument(
     77        "-gt",
     78        "--generate_tests",
     79        action="store_true",
     80        help=""" Generate tests for the package. """,
     81    )
     82
     83    parser.add_argument(
     84        "-uc",
     85        "--update_changelog",
     86        action="store_true",
     87        help=""" Update changelog file. """,
     88    )
     89
     90    parser.add_argument(
     91        "-od",
     92        "--overwrite_dependencies",
     93        action="store_true",
     94        help=""" When building a package, packagelister will be used
     95        to update the dependencies list in pyproject.toml.
     96        The default behavior is to append any new dependencies to
     97        the current list so as not to erase any manually added dependencies
     98        that packagelister may not detect. If you don't have any manually
     99        added dependencies and want to remove any dependencies that your
    100        project no longer uses, pass this flag.""",
    101    )
    102
    103    parser.add_argument(
    104        "-ca",
    105        "--commit_all",
    106        type=str,
    107        default=None,
    108        help=""" Git stage and commit all tracked files with this supplied commit message.
    109        If 'build' is passed, all commits will have message: 'chore: build v{current_version}""",
    110    )
    111
    112    parser.add_argument(
    113        "-s",
    114        "--sync",
    115        action="store_true",
    116        help=""" Pull from github, then push current commit to repo. """,
    117    )
    118
    119    parser.add_argument(
    120        "-dv",
    121        "--dependency_versions",
    122        action="store_true",
    123        help=""" Include version specifiers for dependencies in
    124        pyproject.toml.""",
    125    )
    126
    127    parser.add_argument(
    128        "-up",
    129        "--update",
    130        type=str,
    131        default=None,
    132        choices=["major", "minor", "patch"],
    133        help=""" Excpects one argument: "major", "minor", or "patch".
    134        Passing "-up minor" is equivalent to passing "--build --tag_version --increment_version minor --update_changelog --commit_all build --sync".
    135        To publish the updated package, the -p/--publish switch needs to be added to the cli input.
    136        To install the updated package, the -i/--install switch also needs to be added.""",
    137    )
    138
    139    parser.add_argument(
    140        "-st",
    141        "--skip_tests",
    142        action="store_true",
    143        help=""" Don't run tests when using the -b/--build command. """,
    144    )
    145
    146    args = parser.parse_args()
    147
    148    args.package = Pathier(args.package).resolve()
    149
    150    if args.update:
    151        args.build = True
    152        args.tag_version = True
    153        args.increment_version = args.update
    154        args.update_changelog = True
    155        args.commit_all = "build"
    156        args.sync = True
    157
    158    if args.increment_version and args.increment_version not in [
    159        "major",
    160        "minor",
    161        "patch",
    162    ]:
    163        raise ValueError(
    164            f"Invalid option for -iv/--increment_version: {args.increment_version}"
    165        )
    166
    167    if args.commit_all == "":
    168        raise ValueError("Commit message for args.commit_all cannot be empty.")
    169
    170    if args.publish and not hassle_utilities.on_primary_branch():
    171        print(
    172            "WARNING: You are trying to publish a project that does not appear to be on its main branch."
    173        )
    174        choice = input("Continue? (y/n) ")
    175        if choice != "y":
    176            print("Quitting hassle.")
    177            sys.exit()
    178
    179    return args

def build( package_dir: pathier.pathier.Pathier, skip_tests: bool = False, overwrite_dependencies: bool = False, increment_version: str | None = None): View Source

    182def build(
    183    package_dir: Pathier,
    184    skip_tests: bool = False,
    185    overwrite_dependencies: bool = False,
    186    increment_version: str | None = None,
    187):
    188    """Perform the build process.
    189
    190    Steps:
    191    * Run tests (unless `skip_tests` is `True`)
    192    * Raise error and abandon build if tests fail
    193    * Format source code with `Black`
    194    * Sort source code imports with `isort`
    195    * Update project dependencies in `pyproject.toml`
    196    * Increment version in `pyproject.toml` if `increment_version` supplied
    197    * Generate docs
    198    * Delete previous `dist` folder contents
    199    * Invoke build module"""
    200    if not skip_tests and not run_tests(package_dir):
    201        raise RuntimeError(
    202            f"ERROR: {package_dir.stem} failed testing.\nAbandoning build."
    203        )
    204    hassle_utilities.format_files(package_dir)
    205    [isort.file(path) for path in package_dir.rglob("*.py")]
    206    hassle_utilities.update_dependencies(
    207        package_dir / "pyproject.toml", overwrite_dependencies
    208    )
    209    if increment_version:
    210        hassle_utilities.increment_version(
    211            package_dir / "pyproject.toml", increment_version
    212        )
    213    # Vermin isn't taking into account the minimum version of dependencies.
    214    # Removing from now and defaulting to >=3.10
    215    # hassle_utilities.update_minimum_python_version(pyproject_path)
    216    hassle_utilities.generate_docs(package_dir)
    217    (package_dir / "dist").delete()
    218    os.system(f"{sys.executable} -m build {package_dir}")

Perform the build process.

Steps:

- Run tests (unless `skip_tests` is `True`)
- Raise error and abandon build if tests fail
- Format source code with `Black`
- Sort source code imports with `isort`
- Update project dependencies in `pyproject.toml`
- Increment version in `pyproject.toml` if `increment_version` supplied
- Generate docs
- Delete previous `dist` folder contents
- Invoke build module

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

    221def main(args: argparse.Namespace = None):
    222    if not args:
    223        args = get_args()
    224
    225    pyproject_path = args.package / "pyproject.toml"
    226    args.package.mkcwd()
    227
    228    git = Git()
    229
    230    if not pyproject_path.exists():
    231        raise FileNotFoundError(f"Could not locate pyproject.toml for {args.package}")
    232
    233    if args.generate_tests:
    234        generate_test_files(args.package)
    235
    236    if args.run_tests:
    237        run_tests(args.package)
    238
    239    if args.build:
    240        build(
    241            args.package,
    242            args.skip_tests,
    243            args.overwrite_dependencies,
    244            args.increment_version,
    245        )
    246
    247    if args.increment_version and not args.build:
    248        hassle_utilities.increment_version(pyproject_path, args.increment_version)
    249
    250    if args.commit_all:
    251        if args.commit_all == "build":
    252            version = pyproject_path.loads()["project"]["version"]
    253            args.commit_all = f"chore: build v{version}"
    254        git.add()
    255        git.commit(f'-m "{args.commit_all}"')
    256
    257    if args.tag_version:
    258        hassle_utilities.tag_version(args.package)
    259
    260    if args.update_changelog:
    261        hassle_utilities.update_changelog(pyproject_path)
    262        if args.tag_version:
    263            with git.capture_output():
    264                tags = git.tag("--sort=-committerdate")
    265                most_recent_tag = tags[: tags.find("\n")]
    266                git.execute(f"tag -d {most_recent_tag}")
    267        input("Press enter to continue after manually adjusting the changelog...")
    268        git.commit_files(
    269            [str(args.package / "CHANGELOG.md")], "chore: update changelog"
    270        )
    271        if args.tag_version:
    272            with git.capture_output():
    273                git.tag(most_recent_tag)
    274
    275    if args.publish:
    276        os.system(f"twine upload {args.package / 'dist' / '*'}")
    277
    278    if args.install:
    279        os.system(
    280            f"{sys.executable} -m pip install {args.package} --no-deps --upgrade --no-cache-dir"
    281        )
    282
    283    if args.sync:
    284        git.pull("--tags")
    285        git.push("--tags")