hassle.hassle
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 -i -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.install = 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 170 171 172def build( 173 package_dir: Pathier, skip_tests: bool = False, overwrite_dependencies: bool = False 174): 175 """Perform the build process. 176 177 Steps: 178 * Run tests (unless `skip_tests` is `True`) 179 * Raise error and abandon build if tests fail 180 * Format source code with `Black` 181 * Sort source code imports with `isort` 182 * Update project dependencies in `pyproject.toml` 183 * Generate docs 184 * Delete previous `dist` folder contents 185 * Invoke build module""" 186 if not skip_tests and not run_tests(package_dir): 187 raise RuntimeError( 188 f"ERROR: {package_dir.stem} failed testing.\nAbandoning build." 189 ) 190 hassle_utilities.format_files(package_dir) 191 [isort.file(path) for path in package_dir.rglob("*.py")] 192 hassle_utilities.update_dependencies( 193 package_dir / "pyproject.toml", overwrite_dependencies 194 ) 195 # Vermin isn't taking into account the minimum version of dependencies. 196 # Removing from now and defaulting to >=3.10 197 # hassle_utilities.update_minimum_python_version(pyproject_path) 198 hassle_utilities.generate_docs(package_dir) 199 (package_dir / "dist").delete() 200 os.system(f"{sys.executable} -m build {package_dir}") 201 202 203def main(args: argparse.Namespace = None): 204 if not args: 205 args = get_args() 206 207 pyproject_path = args.package / "pyproject.toml" 208 args.package.mkcwd() 209 210 if not pyproject_path.exists(): 211 raise FileNotFoundError(f"Could not locate pyproject.toml for {args.package}") 212 213 if args.generate_tests: 214 generate_test_files(args.package) 215 216 if args.run_tests: 217 run_tests(args.package) 218 219 if args.increment_version: 220 hassle_utilities.increment_version(pyproject_path, args.increment_version) 221 222 if args.build: 223 build(args.package, args.skip_tests, args.overwrite_dependencies) 224 225 if args.update_changelog: 226 hassle_utilities.update_changelog(pyproject_path) 227 # If we're going to add tag for current version 228 # commit changelog first 229 if args.tag_version: 230 input( 231 "Press enter to continue after optionally pruning the updated changelog..." 232 ) 233 git.commit_files( 234 [str(args.package / "CHANGELOG.md")], "chore: update changelog" 235 ) 236 237 if args.commit_all: 238 if args.commit_all == "build": 239 version = pyproject_path.loads()["project"]["version"] 240 args.commit_all = f"chore: build v{version}" 241 git.add() 242 git.commit(f'-m "{args.commit_all}"') 243 244 if args.tag_version: 245 hassle_utilities.tag_version(args.package) 246 247 if args.publish: 248 os.system(f"twine upload {args.package / 'dist' / '*'}") 249 250 if args.install: 251 os.system( 252 f"{sys.executable} -m pip install {args.package} --no-deps --upgrade --no-cache-dir" 253 ) 254 255 if args.sync: 256 git.pull("--tags") 257 git.push("--tags") 258 259 260if __name__ == "__main__": 261 main(get_args())
def
get_args() -> argparse.Namespace:
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 -i -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.install = 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 return args
def
build( package_dir: pathier.pathier.Pathier, skip_tests: bool = False, overwrite_dependencies: bool = False):
173def build( 174 package_dir: Pathier, skip_tests: bool = False, overwrite_dependencies: bool = False 175): 176 """Perform the build process. 177 178 Steps: 179 * Run tests (unless `skip_tests` is `True`) 180 * Raise error and abandon build if tests fail 181 * Format source code with `Black` 182 * Sort source code imports with `isort` 183 * Update project dependencies in `pyproject.toml` 184 * Generate docs 185 * Delete previous `dist` folder contents 186 * Invoke build module""" 187 if not skip_tests and not run_tests(package_dir): 188 raise RuntimeError( 189 f"ERROR: {package_dir.stem} failed testing.\nAbandoning build." 190 ) 191 hassle_utilities.format_files(package_dir) 192 [isort.file(path) for path in package_dir.rglob("*.py")] 193 hassle_utilities.update_dependencies( 194 package_dir / "pyproject.toml", overwrite_dependencies 195 ) 196 # Vermin isn't taking into account the minimum version of dependencies. 197 # Removing from now and defaulting to >=3.10 198 # hassle_utilities.update_minimum_python_version(pyproject_path) 199 hassle_utilities.generate_docs(package_dir) 200 (package_dir / "dist").delete() 201 os.system(f"{sys.executable} -m build {package_dir}")
Perform the build process.
Steps:
- Run tests (unless
skip_tests
isTrue
) - 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
- Generate docs
- Delete previous
dist
folder contents - Invoke build module
def
main(args: argparse.Namespace = None):
204def main(args: argparse.Namespace = None): 205 if not args: 206 args = get_args() 207 208 pyproject_path = args.package / "pyproject.toml" 209 args.package.mkcwd() 210 211 if not pyproject_path.exists(): 212 raise FileNotFoundError(f"Could not locate pyproject.toml for {args.package}") 213 214 if args.generate_tests: 215 generate_test_files(args.package) 216 217 if args.run_tests: 218 run_tests(args.package) 219 220 if args.increment_version: 221 hassle_utilities.increment_version(pyproject_path, args.increment_version) 222 223 if args.build: 224 build(args.package, args.skip_tests, args.overwrite_dependencies) 225 226 if args.update_changelog: 227 hassle_utilities.update_changelog(pyproject_path) 228 # If we're going to add tag for current version 229 # commit changelog first 230 if args.tag_version: 231 input( 232 "Press enter to continue after optionally pruning the updated changelog..." 233 ) 234 git.commit_files( 235 [str(args.package / "CHANGELOG.md")], "chore: update changelog" 236 ) 237 238 if args.commit_all: 239 if args.commit_all == "build": 240 version = pyproject_path.loads()["project"]["version"] 241 args.commit_all = f"chore: build v{version}" 242 git.add() 243 git.commit(f'-m "{args.commit_all}"') 244 245 if args.tag_version: 246 hassle_utilities.tag_version(args.package) 247 248 if args.publish: 249 os.system(f"twine upload {args.package / 'dist' / '*'}") 250 251 if args.install: 252 os.system( 253 f"{sys.executable} -m pip install {args.package} --no-deps --upgrade --no-cache-dir" 254 ) 255 256 if args.sync: 257 git.pull("--tags") 258 git.push("--tags")