hassle.hassle_utilities

  1import os
  2
  3import packagelister
  4import vermin
  5from pathier import Pathier
  6
  7from hassle import hassle_config
  8
  9root = Pathier(__file__).parent
 10
 11
 12def increment_version(pyproject_path: Pathier, increment_type: str):
 13    """Increment the project.version field in pyproject.toml.
 14
 15    :param package_path: Path to the package/project directory.
 16
 17    :param increment_type: One from 'major', 'minor', or 'patch'."""
 18    meta = pyproject_path.loads()
 19    major, minor, patch = [int(num) for num in meta["project"]["version"].split(".")]
 20    if increment_type == "major":
 21        major += 1
 22        minor = 0
 23        patch = 0
 24    elif increment_type == "minor":
 25        minor += 1
 26        patch = 0
 27    elif increment_type == "patch":
 28        patch += 1
 29    incremented_version = ".".join(str(num) for num in [major, minor, patch])
 30    meta["project"]["version"] = incremented_version
 31    pyproject_path.dumps(meta)
 32
 33
 34def get_minimum_py_version(src: str) -> str:
 35    """Scan src with vermin and return minimum
 36    python version."""
 37    config = vermin.Config()
 38    config.add_backport("typing")
 39    config.add_backport("typing_extensions")
 40    config.set_eval_annotations(True)
 41    result = vermin.visit(src, config).minimum_versions()[1]
 42    return f"{result[0]}.{result[1]}"
 43
 44
 45def get_project_code(project_path: Pathier) -> str:
 46    """Read and return all code from project_path
 47    as one string."""
 48    return "\n".join(file.read_text() for file in project_path.rglob("*.py"))
 49
 50
 51def update_minimum_python_version(pyproject_path: Pathier):
 52    """Use vermin to determine the minimum compatible
 53    Python version and update the corresponding field
 54    in pyproject.toml."""
 55    project_code = get_project_code(pyproject_path.parent / "src")
 56    meta = pyproject_path.loads()
 57    minimum_version = get_minimum_py_version(project_code)
 58    minimum_version = f">={minimum_version}"
 59    meta["project"]["requires-python"] = minimum_version
 60    pyproject_path.dumps(meta)
 61
 62
 63def generate_docs(package_path: Pathier):
 64    """Generate project documentation using pdoc."""
 65    try:
 66        (package_path / "docs").delete()
 67    except Exception as e:
 68        pass
 69    os.system(
 70        f"pdoc -o {package_path / 'docs'} {package_path / 'src' / package_path.stem}"
 71    )
 72
 73
 74def update_dependencies(
 75    pyproject_path: Pathier, overwrite: bool, include_versions: bool = False
 76):
 77    """Update dependencies list in pyproject.toml.
 78
 79    :param overwrite: If True, replace the dependencies in pyproject.toml
 80    with the results of packagelister.scan() .
 81    If False, packages returned by packagelister are appended to
 82    the current dependencies in pyproject.toml if they don't already
 83    exist in the field."""
 84    packages = packagelister.scan(pyproject_path.parent)
 85
 86    packages = [
 87        f"{package}~={packages[package]['version']}"
 88        if packages[package]["version"] and include_versions
 89        else f"{package}"
 90        for package in packages
 91        if package != pyproject_path.parent.stem
 92    ]
 93    packages = [
 94        package.replace("speech_recognition", "speechRecognition")
 95        for package in packages
 96    ]
 97    meta = pyproject_path.loads()
 98    if overwrite:
 99        meta["project"]["dependencies"] = packages
100    else:
101        for package in packages:
102            if "~" in package:
103                name = package.split("~")[0]
104            elif "=" in package:
105                name = package.split("=")[0]
106            else:
107                name = package
108            if all(
109                name not in dependency for dependency in meta["project"]["dependencies"]
110            ):
111                meta["project"]["dependencies"].append(package)
112    pyproject_path.dumps(meta)
113
114
115def update_changelog(pyproject_path: Pathier):
116    """Update project changelog."""
117    meta = pyproject_path.loads()
118    if hassle_config.config_exists():
119        config = hassle_config.load_config()
120    else:
121        hassle_config.warn()
122        print("Creating blank hassle_config.toml...")
123        config = hassle_config.load_config()
124    changelog_path = pyproject_path.parent / "CHANGELOG.md"
125    os.system(
126        f"auto-changelog -p {pyproject_path.parent} --tag-prefix {config['git']['tag_prefix']} --unreleased -v {meta['project']['version']} -o {changelog_path}"
127    )
128    changelog = changelog_path.read_text().splitlines()
129    changelog = [line for line in changelog if "Full set of changes:" not in line]
130    changelog_path.write_text("\n".join(changelog))
131
132
133def tag_version(package_path: Pathier):
134    """Add a git tag corresponding
135    to the version number in pyproject.toml."""
136    if hassle_config.config_exists():
137        tag_prefix = hassle_config.load_config()["git"]["tag_prefix"]
138    else:
139        hassle_config.warn()
140        tag_prefix = ""
141    version = (package_path / "pyproject.toml").loads()["project"]["version"]
142    os.chdir(package_path)
143    os.system(f"git tag {tag_prefix}{version}")
def increment_version(pyproject_path: pathier.pathier.Pathier, increment_type: str):
13def increment_version(pyproject_path: Pathier, increment_type: str):
14    """Increment the project.version field in pyproject.toml.
15
16    :param package_path: Path to the package/project directory.
17
18    :param increment_type: One from 'major', 'minor', or 'patch'."""
19    meta = pyproject_path.loads()
20    major, minor, patch = [int(num) for num in meta["project"]["version"].split(".")]
21    if increment_type == "major":
22        major += 1
23        minor = 0
24        patch = 0
25    elif increment_type == "minor":
26        minor += 1
27        patch = 0
28    elif increment_type == "patch":
29        patch += 1
30    incremented_version = ".".join(str(num) for num in [major, minor, patch])
31    meta["project"]["version"] = incremented_version
32    pyproject_path.dumps(meta)

Increment the project.version field in pyproject.toml.

Parameters
  • package_path: Path to the package/project directory.

  • increment_type: One from 'major', 'minor', or 'patch'.

def get_minimum_py_version(src: str) -> str:
35def get_minimum_py_version(src: str) -> str:
36    """Scan src with vermin and return minimum
37    python version."""
38    config = vermin.Config()
39    config.add_backport("typing")
40    config.add_backport("typing_extensions")
41    config.set_eval_annotations(True)
42    result = vermin.visit(src, config).minimum_versions()[1]
43    return f"{result[0]}.{result[1]}"

Scan src with vermin and return minimum python version.

def get_project_code(project_path: pathier.pathier.Pathier) -> str:
46def get_project_code(project_path: Pathier) -> str:
47    """Read and return all code from project_path
48    as one string."""
49    return "\n".join(file.read_text() for file in project_path.rglob("*.py"))

Read and return all code from project_path as one string.

def update_minimum_python_version(pyproject_path: pathier.pathier.Pathier):
52def update_minimum_python_version(pyproject_path: Pathier):
53    """Use vermin to determine the minimum compatible
54    Python version and update the corresponding field
55    in pyproject.toml."""
56    project_code = get_project_code(pyproject_path.parent / "src")
57    meta = pyproject_path.loads()
58    minimum_version = get_minimum_py_version(project_code)
59    minimum_version = f">={minimum_version}"
60    meta["project"]["requires-python"] = minimum_version
61    pyproject_path.dumps(meta)

Use vermin to determine the minimum compatible Python version and update the corresponding field in pyproject.toml.

def generate_docs(package_path: pathier.pathier.Pathier):
64def generate_docs(package_path: Pathier):
65    """Generate project documentation using pdoc."""
66    try:
67        (package_path / "docs").delete()
68    except Exception as e:
69        pass
70    os.system(
71        f"pdoc -o {package_path / 'docs'} {package_path / 'src' / package_path.stem}"
72    )

Generate project documentation using pdoc.

def update_dependencies( pyproject_path: pathier.pathier.Pathier, overwrite: bool, include_versions: bool = False):
 75def update_dependencies(
 76    pyproject_path: Pathier, overwrite: bool, include_versions: bool = False
 77):
 78    """Update dependencies list in pyproject.toml.
 79
 80    :param overwrite: If True, replace the dependencies in pyproject.toml
 81    with the results of packagelister.scan() .
 82    If False, packages returned by packagelister are appended to
 83    the current dependencies in pyproject.toml if they don't already
 84    exist in the field."""
 85    packages = packagelister.scan(pyproject_path.parent)
 86
 87    packages = [
 88        f"{package}~={packages[package]['version']}"
 89        if packages[package]["version"] and include_versions
 90        else f"{package}"
 91        for package in packages
 92        if package != pyproject_path.parent.stem
 93    ]
 94    packages = [
 95        package.replace("speech_recognition", "speechRecognition")
 96        for package in packages
 97    ]
 98    meta = pyproject_path.loads()
 99    if overwrite:
100        meta["project"]["dependencies"] = packages
101    else:
102        for package in packages:
103            if "~" in package:
104                name = package.split("~")[0]
105            elif "=" in package:
106                name = package.split("=")[0]
107            else:
108                name = package
109            if all(
110                name not in dependency for dependency in meta["project"]["dependencies"]
111            ):
112                meta["project"]["dependencies"].append(package)
113    pyproject_path.dumps(meta)

Update dependencies list in pyproject.toml.

Parameters
  • overwrite: If True, replace the dependencies in pyproject.toml with the results of packagelister.scan() . If False, packages returned by packagelister are appended to the current dependencies in pyproject.toml if they don't already exist in the field.
def update_changelog(pyproject_path: pathier.pathier.Pathier):
116def update_changelog(pyproject_path: Pathier):
117    """Update project changelog."""
118    meta = pyproject_path.loads()
119    if hassle_config.config_exists():
120        config = hassle_config.load_config()
121    else:
122        hassle_config.warn()
123        print("Creating blank hassle_config.toml...")
124        config = hassle_config.load_config()
125    changelog_path = pyproject_path.parent / "CHANGELOG.md"
126    os.system(
127        f"auto-changelog -p {pyproject_path.parent} --tag-prefix {config['git']['tag_prefix']} --unreleased -v {meta['project']['version']} -o {changelog_path}"
128    )
129    changelog = changelog_path.read_text().splitlines()
130    changelog = [line for line in changelog if "Full set of changes:" not in line]
131    changelog_path.write_text("\n".join(changelog))

Update project changelog.

def tag_version(package_path: pathier.pathier.Pathier):
134def tag_version(package_path: Pathier):
135    """Add a git tag corresponding
136    to the version number in pyproject.toml."""
137    if hassle_config.config_exists():
138        tag_prefix = hassle_config.load_config()["git"]["tag_prefix"]
139    else:
140        hassle_config.warn()
141        tag_prefix = ""
142    version = (package_path / "pyproject.toml").loads()["project"]["version"]
143    os.chdir(package_path)
144    os.system(f"git tag {tag_prefix}{version}")

Add a git tag corresponding to the version number in pyproject.toml.