Publish to PyPI

This commit is contained in:
Augusto Gunsch 2022-11-11 23:07:41 +01:00
parent a7c9615c7a
commit 8818b0ddcd
No known key found for this signature in database
GPG Key ID: F7EEFE29825C72DC
8 changed files with 221 additions and 0 deletions

4
.gitignore vendored
View File

@ -1 +1,5 @@
venv/ venv/
*.egg-info/
__pycache__/
*.test_*.srt
dist/

10
Makefile Normal file
View File

@ -0,0 +1,10 @@
dist: clean setup.cfg pyproject.toml
python3.9 -m build
upload: dist
python3.9 -m twine upload --repository pypi dist/*
clean:
rm -rfv src/subprompt.egg-info
rm -rfv dist
rm -rfv src/subprompt/__pycache__

View File

@ -3,3 +3,7 @@ Substitute every match of a regex in multiple files - with confirmation prompts.
``` ```
subprompt [REGEX] [SUB] [FILES...] subprompt [REGEX] [SUB] [FILES...]
``` ```
Install through PyPi:
```
python3.9 -m pip install subprompt
```

6
pyproject.toml Normal file
View File

@ -0,0 +1,6 @@
[build-system]
requires = [
"setuptools>=42",
"wheel"
]
build-backend = "setuptools.build_meta"

35
setup.cfg Normal file
View File

@ -0,0 +1,35 @@
[metadata]
name = subprompt
version = 0.0.3
author = Augusto Lenz Gunsch
author_email = augusto@augustogunsch.com
description = Substitute Regex in files with prompt confirmation
long_description = file: README.md
long_description_content_type = text/markdown
url = https://git.augustogunsch.com/augusto/subprompt
project_urls =
Bug Tracker = https://git.augustogunsch.com/augusto/subprompt/issues
classifiers =
Programming Language :: Python :: 3
License :: OSI Approved :: GNU General Public License v3 (GPLv3)
Operating System :: OS Independent
Development Status :: 6 - Mature
Topic :: Terminals
Topic :: Text Processing :: General
Topic :: Text Processing :: Filters
Topic :: Utilities
[options]
package_dir =
= src
install_requires =
colorama>=0.4.3
packages = find:
python_requires = >=3.9
[options.packages.find]
where = src
[options.entry_points]
console_scripts =
subprompt = subprompt.subprompt:main

View File

@ -0,0 +1,6 @@
import subprompt
# External interface
def run(*args):
subprompt.run(args)

156
src/subprompt/subprompt.py Normal file
View File

@ -0,0 +1,156 @@
#!/bin/python3.9
#
# subprompt is a script to replace matches of a Regex across files
# with a confirmation prompt.
# Copyright (C) 2022 Augusto Lenz Gunsch
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
#
#
# Contact information available in my website: https://augustogunsch.com
#
import re
import hashlib
import os
from sys import argv, stderr
from os.path import basename
from colorama import init, Fore, Back, Style
init(autoreset=True)
class Filter:
def __init__(self, regex, sub):
self.replace_all = False
self.quit_loop = False
self.sub_count = 0
self.match_count = 0
self.regex = re.compile(regex)
self.sub = sub
def _prompt(self, matchobj, line, line_n, fname):
curr_match = matchobj.group(0)
if self.quit_loop:
return curr_match
replaced_match = self.regex.sub(self.sub, curr_match)
if replaced_match == curr_match:
return replaced_match
self.match_count += 1
if self.replace_all:
self.sub_count += 1
return replaced_match
highlighted = line.replace(curr_match,
Back.RED + curr_match + Back.RESET)
replaced = line.replace(curr_match,
Back.YELLOW + Fore.BLACK + replaced_match + Back.RESET + Fore.RESET)
print(Fore.GREEN + Style.BRIGHT + fname)
print(Fore.YELLOW + Style.BRIGHT + str(line_n), end='')
print(':{0}'.format(highlighted))
print('Becomes the following:')
print(Fore.YELLOW + Style.BRIGHT + str(line_n), end='')
print(':{0}'.format(replaced))
print()
while True:
answer = input('Confirm the change? [Y/n/a/q] ').lower()
if answer in ['y', 'yes', '']:
self.sub_count += 1
print()
return replaced_match
elif answer in ['n', 'no']:
print()
return curr_match
elif answer in ['a', 'all']:
self.sub_count += 1
self.replace_all = True
return replaced_match
elif answer in ['q', 'quit']:
self.quit_loop = True
return curr_match
print('Invalid answer. Please type again.')
def filter_file(self, fname):
with open(fname, 'r') as file:
contents = file.read()
lines = contents.splitlines()
lines = [
self.regex.sub(
lambda matchobj: self._prompt(matchobj, line, line_n, fname),
line
)
for (line_n, line) in enumerate(lines)
]
new_contents = '\n'.join(lines)
hash_old = hashlib.md5(contents.encode())
hash_new = hashlib.md5(new_contents.encode())
if hash_old.digest() != hash_new.digest():
with open(fname, 'w') as file:
file.write(new_contents)
file.write('\n')
def run(args):
if len(args) < 3:
print('usage: {0} [REGEX] [SUB] [FILES...]'.format(basename(__file__)), file=stderr)
exit(1)
regex = args[0]
sub = args[1]
files = args[2:]
filter_obj = Filter(regex, sub)
for file in files:
# Check if file is writable
if os.access(file, os.W_OK):
filter_obj.filter_file(file)
if filter_obj.quit_loop:
break
if filter_obj.match_count == 0:
print(Fore.RED + Style.BRIGHT + 'No matches found.')
else:
print(Fore.YELLOW + Style.BRIGHT + 'Made {0} of {1} substitutions.'
.format(Fore.WHITE + str(filter_obj.sub_count) + Fore.YELLOW,
Fore.WHITE + str(filter_obj.match_count) + Fore.YELLOW))
def main():
argv.pop(0)
run(argv)
if __name__ == '__main__':
main()

View File