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

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

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

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