skilleter-thingy 0.3.14__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. skilleter_thingy/__init__.py +0 -0
  2. skilleter_thingy/addpath.py +107 -0
  3. skilleter_thingy/console_colours.py +63 -0
  4. skilleter_thingy/ffind.py +535 -0
  5. skilleter_thingy/ggit.py +88 -0
  6. skilleter_thingy/ggrep.py +155 -0
  7. skilleter_thingy/git_br.py +186 -0
  8. skilleter_thingy/git_ca.py +147 -0
  9. skilleter_thingy/git_cleanup.py +297 -0
  10. skilleter_thingy/git_co.py +227 -0
  11. skilleter_thingy/git_common.py +68 -0
  12. skilleter_thingy/git_hold.py +162 -0
  13. skilleter_thingy/git_parent.py +84 -0
  14. skilleter_thingy/git_retag.py +67 -0
  15. skilleter_thingy/git_review.py +1450 -0
  16. skilleter_thingy/git_update.py +398 -0
  17. skilleter_thingy/git_wt.py +72 -0
  18. skilleter_thingy/gitcmp_helper.py +328 -0
  19. skilleter_thingy/gitprompt.py +293 -0
  20. skilleter_thingy/linecount.py +154 -0
  21. skilleter_thingy/multigit.py +915 -0
  22. skilleter_thingy/py_audit.py +133 -0
  23. skilleter_thingy/remdir.py +127 -0
  24. skilleter_thingy/rpylint.py +98 -0
  25. skilleter_thingy/strreplace.py +82 -0
  26. skilleter_thingy/test.py +34 -0
  27. skilleter_thingy/tfm.py +948 -0
  28. skilleter_thingy/tfparse.py +101 -0
  29. skilleter_thingy/trimpath.py +82 -0
  30. skilleter_thingy/venv_create.py +47 -0
  31. skilleter_thingy/venv_template.py +47 -0
  32. skilleter_thingy/xchmod.py +124 -0
  33. skilleter_thingy/yamlcheck.py +89 -0
  34. skilleter_thingy-0.3.14.dist-info/METADATA +606 -0
  35. skilleter_thingy-0.3.14.dist-info/RECORD +39 -0
  36. skilleter_thingy-0.3.14.dist-info/WHEEL +5 -0
  37. skilleter_thingy-0.3.14.dist-info/entry_points.txt +31 -0
  38. skilleter_thingy-0.3.14.dist-info/licenses/LICENSE +619 -0
  39. skilleter_thingy-0.3.14.dist-info/top_level.txt +1 -0
@@ -0,0 +1,133 @@
1
+ #!/usr/bin/env python3
2
+
3
+ """ Query api.osv.dev to determine whether a specified version of a particular
4
+ Python package is subject to known security vulnerabilities """
5
+
6
+ ################################################################################
7
+
8
+ import sys
9
+ import requests
10
+ import subprocess
11
+ import tempfile
12
+ import re
13
+ import glob
14
+ import argparse
15
+
16
+ ################################################################################
17
+
18
+ PIP_PACKAGES = ('pip', 'pkg_resources', 'setuptools')
19
+ PIP_OPTIONS = '--no-cache-dir'
20
+
21
+ QUERY_URL = "https://api.osv.dev/v1/query"
22
+ QUERY_HEADERS = {'content-type': 'application/json', 'Accept-Charset': 'UTF-8'}
23
+
24
+ ANSI_NORMAL = '\x1b[0m'
25
+ ANSI_BOLD = '\x1b[1m'
26
+
27
+ ################################################################################
28
+
29
+ def audit(package, version):
30
+ """ Main function """
31
+
32
+ # Query api.osv.dev for known vulnerabilties in this version of the package
33
+
34
+ payload = '{"version": "'+version+'", "package": {"name": "'+package+'", "ecosystem": "PyPI"}}'
35
+ result = requests.post(QUERY_URL, data=payload, headers=QUERY_HEADERS)
36
+
37
+ # Parse and report the results
38
+
39
+ details = result.json()
40
+
41
+ print('-' * 80)
42
+ if package in PIP_PACKAGES:
43
+ print(f'Package: {package} {version} (part of Pip)')
44
+ else:
45
+ print(f'Package: {package} {version}')
46
+
47
+ print()
48
+
49
+ if 'vulns' in details:
50
+ print(f'{len(details["vulns"])} known vulnerabilities')
51
+
52
+ for v in details['vulns']:
53
+ print()
54
+ print(f'{ANSI_BOLD}Vulnerability: {v["id"]}{ANSI_NORMAL}')
55
+
56
+ if 'summary' in v:
57
+ print(f'Summary: {v["summary"]}')
58
+
59
+ if 'aliases' in v:
60
+ print('Aliases: %s' % (', '.join(v['aliases'])))
61
+
62
+ if 'details' in v:
63
+ print()
64
+ print(v['details'])
65
+ else:
66
+ print('No known vulnerabilities')
67
+
68
+ ################################################################################
69
+
70
+ def main():
71
+ """ Entry point """
72
+
73
+ parser = argparse.ArgumentParser(
74
+ description='Query api.osv.dev to determine whether Python packagers in a requirments.txt file are subject to known security vulnerabilities')
75
+ parser.add_argument('requirements', nargs='*', type=str, action='store',
76
+ help='The requirements file (if not specified, then the script searches for a requirements.txt file)')
77
+ args = parser.parse_args()
78
+
79
+ requirements = args.requirements or glob.glob('**/requirements.txt', recursive=True)
80
+
81
+ if not requirements:
82
+ print('No requirements.txt file(s) found')
83
+ sys.exit(0)
84
+
85
+ # Create a venv for each requirements file, install pip and the packages
86
+ # and prerequisites, get the list of installed package versions
87
+ # and check each one.
88
+
89
+ for requirement in requirements:
90
+ print('=' * 80)
91
+ print(f'{ANSI_BOLD}File: {requirement}{ANSI_NORMAL}')
92
+ print()
93
+
94
+ with tempfile.TemporaryDirectory() as env_dir:
95
+ with tempfile.NamedTemporaryFile() as package_list:
96
+ script = f'python3 -m venv {env_dir}' \
97
+ f' && . {env_dir}/bin/activate' \
98
+ f' && python3 -m pip {PIP_OPTIONS} install --upgrade pip setuptools' \
99
+ f' && python3 -m pip {PIP_OPTIONS} install -r {requirement}' \
100
+ f' && python3 -m pip {PIP_OPTIONS} list | tail -n+3 | tee {package_list.name}' \
101
+ ' && deactivate'
102
+
103
+ try:
104
+ subprocess.run(script, check=True, shell=True)
105
+
106
+ except subprocess.CalledProcessError as exc:
107
+ print(f'ERROR #{exc.returncode}: {exc.stdout}')
108
+ sys.exit(exc.returncode)
109
+
110
+ with open(package_list.name) as infile:
111
+ for package in infile.readlines():
112
+ package_info = re.split(' +', package.strip())
113
+
114
+ audit(package_info[0], package_info[1])
115
+
116
+ ################################################################################
117
+
118
+ def py_audit():
119
+ """Entry point"""
120
+
121
+ try:
122
+ main()
123
+
124
+ except KeyboardInterrupt:
125
+ sys.exit(1)
126
+
127
+ except BrokenPipeError:
128
+ sys.exit(2)
129
+
130
+ ################################################################################
131
+
132
+ if __name__ == '__main__':
133
+ py_audit()
@@ -0,0 +1,127 @@
1
+ #! /usr/bin/env python3
2
+
3
+ ################################################################################
4
+ """ remdir - remove empty directories
5
+
6
+ Given the name of a directory tree and, optionally, a list of files to ignore,
7
+ recursively deletes any directory in the tree that is either completely empty
8
+ or contains nothing but files matching the list.
9
+
10
+ For example:
11
+
12
+ remdir /backup --ignore '*.bak' --keep .stfolder
13
+
14
+ would remove any directory within the /backup tree that is empty
15
+ or contains only files that match the '*.bak' wildcard so long as
16
+ it isn't called '.stfolder'.
17
+
18
+ TODO: Using os.walk() means that, if a directory is deleted, it still shows
19
+ up in the list of directories in the parent directory (?)
20
+ """
21
+ ################################################################################
22
+
23
+ import sys
24
+ import os
25
+ import argparse
26
+ import fnmatch
27
+ import shutil
28
+ import logging
29
+
30
+ from skilleter_modules import colour
31
+
32
+ ################################################################################
33
+
34
+ def main():
35
+ """ Entry point """
36
+
37
+ # Parse the command line
38
+
39
+ parser = argparse.ArgumentParser(description='Remove empty directories')
40
+ parser.add_argument('--dry-run', '-D', action='store_true', help='Dry-run - report what would be done without doing anything')
41
+ parser.add_argument('--debug', action='store_true', help='Output debug information')
42
+ parser.add_argument('--verbose', action='store_true', help='Output verbose information')
43
+ parser.add_argument('--ignore', '-I', action='append', help='Files to ignore when considering whether a directory is empty')
44
+ parser.add_argument('--keep', '-K', action='append', help='Directories that should be kept even if they are empty')
45
+ parser.add_argument('dirs', nargs='+', help='Directories to prune')
46
+ args = parser.parse_args()
47
+
48
+ if args.debug:
49
+ logging.basicConfig(level=logging.DEBUG)
50
+
51
+ if not args.keep:
52
+ args.keep = []
53
+
54
+ # Go through each directory
55
+
56
+ for directory in args.dirs:
57
+ if not os.path.isdir(directory):
58
+ colour.write(f'"{directory}" is not a directory')
59
+ continue
60
+
61
+ # Walk through the directory tree in bottom-up order
62
+
63
+ for root, dirs, files in os.walk(directory, topdown=False):
64
+ logging.debug('')
65
+ logging.debug('Directory: %s', root)
66
+ logging.debug(' Sub-directories : %s', dirs)
67
+ logging.debug(' Files : %s', files)
68
+
69
+ # Only consider directories with no subdirectories
70
+
71
+ if dirs:
72
+ logging.debug('Ignoring directory "%s" as it has %d subdirectories', root, len(dirs))
73
+ else:
74
+ # Count of files (if any) to preserve in the directory
75
+
76
+ filecount = len(files)
77
+
78
+ # If any file matches an entry in the ignore list (if we have one) then decrement the file count
79
+
80
+ if args.ignore:
81
+ for file in files:
82
+ for ignore in args.ignore:
83
+ if fnmatch.fnmatch(file, ignore):
84
+ filecount -= 1
85
+ break
86
+
87
+ # If no non-matching files then delete the directory unless it is in the keep list
88
+
89
+ if filecount == 0:
90
+ keep_dir = False
91
+ for keep in args.keep:
92
+ if fnmatch.fnmatch(os.path.basename(root), keep):
93
+ keep_dir = True
94
+ break
95
+
96
+ if keep_dir:
97
+ colour.write(f'Keeping empty directory [BLUE:{root}]')
98
+ else:
99
+ logging.debug('Deleting directory: %s', root)
100
+ colour.write(f'Deleting "[BLUE:{root}]"')
101
+
102
+ if not args.dry_run:
103
+ # Delete the directory and contents
104
+
105
+ try:
106
+ shutil.rmtree(root)
107
+ except OSError:
108
+ colour.error('Unable to delete "[BLUE:{root}]"')
109
+ else:
110
+ logging.debug('Ignoring directory "%s" as it has %d non-ignorable files', root, filecount)
111
+
112
+ ################################################################################
113
+
114
+ def remdir():
115
+ """Entry point"""
116
+
117
+ try:
118
+ main()
119
+ except KeyboardInterrupt:
120
+ sys.exit(1)
121
+ except BrokenPipeError:
122
+ sys.exit(2)
123
+
124
+ ################################################################################
125
+
126
+ if __name__ == '__main__':
127
+ remdir()
@@ -0,0 +1,98 @@
1
+ #! /usr/bin/env python3
2
+
3
+ ################################################################################
4
+ """ Run pylint on all the Python source files in the current tree
5
+
6
+ Copyright (C) 2017-18 John Skilleter """
7
+ ################################################################################
8
+
9
+ import os
10
+ import sys
11
+ import argparse
12
+ import glob
13
+ import subprocess
14
+
15
+ ################################################################################
16
+
17
+ PYLINT = 'pylint'
18
+
19
+ ################################################################################
20
+
21
+ def main():
22
+ """ Main code. Exits directly on failure to locate source files, or returns
23
+ the status code from Pylint otherwise. """
24
+
25
+ # Parse the command line
26
+
27
+ parser = argparse.ArgumentParser(description='Run pylint in the current (or specified) directory/ies')
28
+
29
+ parser.add_argument('paths', nargs='*', help='List of files or paths to lint')
30
+
31
+ args = parser.parse_args()
32
+
33
+ if not args.paths:
34
+ args.paths = ['.']
35
+
36
+ sourcefiles = []
37
+
38
+ # Use rgrep to find source files that have a Python 3 hashbang
39
+
40
+ for entry in args.paths:
41
+ if os.path.isdir(entry):
42
+ result = subprocess.run(['rgrep', '-E', '--exclude-dir=.git', '-l', '#![[:space:]]*/usr/bin/(env[[:space:]])?python3'] + args.paths,
43
+ capture_output=True, check=False, text=True)
44
+
45
+ if result.returncode == 1:
46
+ sys.stderr.write('No Python3 source files found\n')
47
+ sys.exit(2)
48
+
49
+ if result.returncode:
50
+ sys.stderr.write(f'ERROR #{result.returncode}: {result.stderr}')
51
+ sys.exit(1)
52
+
53
+ sourcefiles += result.stdout.split('\n')
54
+
55
+ elif os.path.isfile(entry):
56
+ sourcefiles.append(entry)
57
+ else:
58
+ files = glob.glob(entry)
59
+
60
+ if not files:
61
+ sys.stderr.write(f'No files found matching "{entry}"')
62
+ sys.exit(2)
63
+
64
+ sourcefiles += files
65
+
66
+ # Run pylint on all the files
67
+
68
+ try:
69
+ result = subprocess.run([PYLINT, '--output-format', 'parseable'] + sourcefiles, capture_output=False, check=False, text=True)
70
+
71
+ except FileNotFoundError:
72
+ sys.stderr.write(f'Unable to locate {PYLINT}\n')
73
+ sys.exit(1)
74
+
75
+ if result.returncode >= 64:
76
+ sys.stderr.write(f'Unexpected error: {result.returncode}\n')
77
+ sys.exit(3)
78
+
79
+ # Function return code is the status return from pylint
80
+
81
+ return result.returncode
82
+
83
+ ################################################################################
84
+
85
+ def rpylint():
86
+ """Entry point"""
87
+
88
+ try:
89
+ sys.exit(main())
90
+ except KeyboardInterrupt:
91
+ sys.exit(1)
92
+ except BrokenPipeError:
93
+ sys.exit(2)
94
+
95
+ ################################################################################
96
+
97
+ if __name__ == '__main__':
98
+ rpylint()
@@ -0,0 +1,82 @@
1
+ #! /usr/bin/env python3
2
+
3
+ ################################################################################
4
+ """ Textual search and replace
5
+
6
+ For those occasions when you want to search and replace strings with
7
+ regexppy characters that upset sed.
8
+
9
+ Copyright (C) 2018 John Skilleter """
10
+ ################################################################################
11
+
12
+ import os
13
+ import sys
14
+ import argparse
15
+ import tempfile
16
+
17
+ ################################################################################
18
+
19
+ def main():
20
+ """ Main function """
21
+
22
+ parser = argparse.ArgumentParser(description='Textual search and replace')
23
+ parser.add_argument('--inplace', '-i', action='store_true', help='Do an in-place search and replace on the input file')
24
+ parser.add_argument('search', nargs=1, action='store', help='Search text')
25
+ parser.add_argument('replace', nargs=1, action='store', help='Replacment text')
26
+ parser.add_argument('infile', nargs='?', action='store', help='Input file')
27
+ parser.add_argument('outfile', nargs='?', action='store', help='Output file')
28
+
29
+ args = parser.parse_args()
30
+
31
+ # Sanity check
32
+
33
+ if args.inplace and not args.infile or args.outfile:
34
+ print('For in-place operations you must specify and input file and no output file')
35
+
36
+ # Open the input file
37
+
38
+ if args.infile:
39
+ infile = open(args.infile, 'r')
40
+ else:
41
+ infile = sys.stdin
42
+
43
+ # Open the output file, using a temporary file in the same directory as the input file
44
+ # if we are doing in-place operations
45
+
46
+ if args.outfile:
47
+ outfile = open(args.outfile, 'w')
48
+ elif args.inplace:
49
+ outfile = tempfile.NamedTemporaryFile(mode='w', delete=False, dir=os.path.dirname(args.infile))
50
+ else:
51
+ outfile = sys.stdout
52
+
53
+ # Perform the searchy-replacey-ness
54
+
55
+ for data in infile:
56
+ outfile.write(data.replace(args.search[0], args.replace[0]))
57
+
58
+ # If we doing in-place then juggle the temporary and input files
59
+
60
+ if args.inplace:
61
+ mode = os.stat(args.infile).st_mode
62
+ outfile.close()
63
+ infile.close()
64
+ os.rename(outfile.name, args.infile)
65
+ os.chmod(args.infile, mode)
66
+
67
+ ################################################################################
68
+
69
+ def strreplace():
70
+ """Entry point"""
71
+
72
+ try:
73
+ main()
74
+ except KeyboardInterrupt:
75
+ sys.exit(1)
76
+ except BrokenPipeError:
77
+ sys.exit(2)
78
+
79
+ ################################################################################
80
+
81
+ if __name__ == '__main__':
82
+ strreplace()
@@ -0,0 +1,34 @@
1
+ #!/usr/bin/env python3
2
+
3
+ print('Importing all Skilleter-thingy modules')
4
+ import addpath
5
+ import console_colours
6
+ import ffind
7
+ import ggit
8
+ import ggrep
9
+ import git_br
10
+ import git_ca
11
+ import git_cleanup
12
+ import git_co
13
+ import git_common
14
+ import git_hold
15
+ import git_parent
16
+ import git_retag
17
+ import git_review
18
+ import git_update
19
+ import git_wt
20
+ import gitcmp_helper
21
+ import gitprompt
22
+ import linecount
23
+ import multigit
24
+ import py_audit
25
+ import remdir
26
+ import rpylint
27
+ import strreplace
28
+ import tfm
29
+ import tfparse
30
+ import trimpath
31
+ import venv_create
32
+ import xchmod
33
+ import yamlcheck
34
+ print('Import successful')