From 5d629d4bec2728235e01edb50553d8e89a70356d Mon Sep 17 00:00:00 2001 From: Jason Hall Date: Thu, 24 Jun 2021 16:21:57 -0400 Subject: [PATCH] refactored into an installable package --- .gitignore | 145 +++++++++++++++++++++++++++ CHANGELOG.rst | 3 + pyproject.toml | 6 ++ requirements.txt | 2 - setup.cfg | 32 ++++++ py_wype.py => src/pywype/__init__.py | 114 +++++++++++---------- src/pywype/command_line.py | 15 +++ tests/test_is_linux.py | 4 + 8 files changed, 266 insertions(+), 55 deletions(-) create mode 100644 .gitignore create mode 100644 pyproject.toml delete mode 100644 requirements.txt create mode 100644 setup.cfg rename py_wype.py => src/pywype/__init__.py (61%) mode change 100755 => 100644 create mode 100644 src/pywype/command_line.py diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..eccb2ce --- /dev/null +++ b/.gitignore @@ -0,0 +1,145 @@ +# Created by https://www.toptal.com/developers/gitignore/api/python +# Edit at https://www.toptal.com/developers/gitignore?templates=python + +### Python ### +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +cover/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +.pybuilder/ +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +# For a library or package, you might want to ignore these files since the code is +# intended to run in multiple environments; otherwise, check them in: +# .python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ +.testenv/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# Cython debug symbols +cython_debug/ + +# End of https://www.toptal.com/developers/gitignore/api/python diff --git a/CHANGELOG.rst b/CHANGELOG.rst index df660f0..bc3d9f0 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -11,4 +11,7 @@ This document records all notable changes to pyWype. - Refactor - Add support for SD cards +**0.3 (06.24.2021)** + +- refactored into an installable package diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..b5a3c46 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,6 @@ +[build-system] +requires = [ + "setuptools>=42", + "wheel" +] +build-backend = "setuptools.build_meta" \ No newline at end of file diff --git a/requirements.txt b/requirements.txt deleted file mode 100644 index 954c95c..0000000 --- a/requirements.txt +++ /dev/null @@ -1,2 +0,0 @@ -future==0.16.0 -regex==2017.7.28 diff --git a/setup.cfg b/setup.cfg new file mode 100644 index 0000000..025591d --- /dev/null +++ b/setup.cfg @@ -0,0 +1,32 @@ +[metadata] +name = pywype +version = 0.3 +author = Jason Hall +author_email = jdhall75@gmail.com +description = Securely wipe storage drives and partitions +long_description = file: README.rst +long_description_content_type = text/rst +url = https://github.com/jdhall75/pyWype +project_urls = + Bug Tracker = https://github.com/jdhall75/pywype/issues +classifiers = + Programming Language :: Python :: 3 + License :: OSI Approved :: MIT License + Operating System :: OS Independent + +[options] +package_dir = + = src +packages = find: +python_requires = >=3.6 +install_requires = + future==0.16.0 + regex==2017.7.28 + + +[options.packages.find] +where = src + +[options.entry_points] +console_scripts = + pywype = pywype.command_line:main \ No newline at end of file diff --git a/py_wype.py b/src/pywype/__init__.py old mode 100755 new mode 100644 similarity index 61% rename from py_wype.py rename to src/pywype/__init__.py index 29df7ec..621e1e4 --- a/py_wype.py +++ b/src/pywype/__init__.py @@ -1,8 +1,3 @@ -#!/usr/bin/env python3 - -"""Disk-wiping utility for GNU/Linux, written in Python 2 & 3. -""" - from __future__ import print_function import os @@ -10,44 +5,52 @@ import re import sys +VERSION = 0.3 + +"""Disk-wiping utility for GNU/Linux, written in Python 2 & 3. +""" + try: input = raw_input except NameError: pass + def is_linux(): - """Check if system is 'Linux' - """ + """Check if system is 'Linux'""" - if 'Linux' not in platform.system(): + if "Linux" not in platform.system(): print("This program was designed for GNU/Linux. Exiting.") sys.exit() + def root_user_check(): - """Check if current UID is 0. - """ + """Check if current UID is 0.""" if os.getuid() != 0: print("This program requires ROOT privileges. Exiting.") sys.exit() + def list_mounted_devices(): - """List mounted device(s) / partition(s). - """ + """List mounted device(s) / partition(s).""" print(22 * "-", "DEVICES & PARTITIONS", 22 * "-") - return os.system('lsblk /dev/sd* --nodeps --output NAME,MODEL,VENDOR,SIZE,TYPE,STATE') + return os.system( + "lsblk /dev/sd* --nodeps --output NAME,MODEL,VENDOR,SIZE,TYPE,STATE" + ) + def define_device_to_wipe(): - """Prompt user to define device or partition to wipe. - """ + """Prompt user to define device or partition to wipe.""" while True: try: device = input( "Enter letter [number] of device/partition to wipe," - "\ne.g. to wipe '/dev/sdb1' enter 'b1': ") + "\ne.g. to wipe '/dev/sdb1' enter 'b1': " + ) if not re.match("^[a-z][0-9]?$", device): raise ValueError() @@ -56,21 +59,23 @@ def define_device_to_wipe(): except ValueError: print("Sorry, that's not a valid device or partition. Try again.") + def append_device_to_wipe(): - """Append user-defined device/partition to /dev/sd. - """ + """Append user-defined device/partition to /dev/sd.""" letter = define_device_to_wipe() - return '/dev/sd' + letter + return "/dev/sd" + letter + def number_of_wipes(): - """Prompt user for number of wipes to perform. - """ + """Prompt user for number of wipes to perform.""" while True: try: - wipes = int(input("How many times do you want to wipe the device or partition?: ")) + wipes = int( + input("How many times do you want to wipe the device or partition?: ") + ) if wipes <= 0: raise ValueError() @@ -79,9 +84,9 @@ def number_of_wipes(): except ValueError: print("Sorry, that's not a valid number. Try again: ") + def confirm_wipe(): - """Prompt user to confirm disk erasure. - """ + """Prompt user to confirm disk erasure.""" print("WARNING!!! WRITING CHANGES TO DISK WILL RESULT IN IRRECOVERABLE DATA LOSS.") @@ -89,18 +94,18 @@ def confirm_wipe(): try: reply = input("Do you want to proceed? (Yes/No): ").lower().strip() - if reply == 'yes': + if reply == "yes": return True - if reply == 'no': + if reply == "no": print("Exiting pyWype.") sys.exit() except ValueError: print("Sorry, that's not a valid entry. Try again: ") + def write_zeros_to_device(): - """Write zeros to device/partition. - """ + """Write zeros to device/partition.""" append = append_device_to_wipe() num = number_of_wipes() @@ -108,12 +113,16 @@ def write_zeros_to_device(): for i in range(num): print("Processing pass count {} of {} ... ".format(i + 1, num)) - os.system(('dd if=/dev/zero |pv --progress --time --rate --bytes|' - 'dd of={} bs=1024'.format(append))) + os.system( + ( + "dd if=/dev/zero |pv --progress --time --rate --bytes|" + "dd of={} bs=1024".format(append) + ) + ) + def write_random_to_device(): - """Write random zeros and ones to device/partition. - """ + """Write random zeros and ones to device/partition.""" append = append_device_to_wipe() num = number_of_wipes() @@ -121,12 +130,16 @@ def write_random_to_device(): for i in range(num): print("Processing pass count {} of {} ... ".format(i + 1, num)) - os.system(('dd if=/dev/urandom |pv --progress --time --rate --bytes|' - 'dd of={} bs=1024'.format(append))) + os.system( + ( + "dd if=/dev/urandom |pv --progress --time --rate --bytes|" + "dd of={} bs=1024".format(append) + ) + ) + def menu(): - """Menu prompt for use to select program option. - """ + """Menu prompt for use to select program option.""" list_mounted_devices() @@ -134,44 +147,39 @@ def menu(): try: print(30 * "-", "MENU", 30 * "-") print("1. Overwrite device or partition with 0's \n(faster, less secure).") - print("2. Overwrite device or partition with random 0\'s & 1\'s" - "\n(slower, more secure).") + print( + "2. Overwrite device or partition with random 0's & 1's" + "\n(slower, more secure)." + ) print("3. Quit.") choice = input("Select an option (1, 2 or 3): ") - if choice not in ('1', '2', '3'): + if choice not in ("1", "2", "3"): raise ValueError() return choice except ValueError: print("Sorry, that's not a valid number. Try again: ") + def interactive_mode(): - """Display menu-driven options and run function based on selection. - """ + """Display menu-driven options and run function based on selection.""" while True: choice = menu() - if choice == '3': + if choice == "3": sys.exit() - elif choice == '1': + elif choice == "1": write_zeros_to_device() - elif choice == '2': + elif choice == "2": write_random_to_device() + def wipe_device(): - """Program to wipe drive. - """ + """Program to wipe drive.""" is_linux() root_user_check() interactive_mode() - -if __name__ == '__main__': - print(28 * '-', " pyWype ", 28 * '-') - print("PYTHON DISK & PARTITION WIPING UTILITY FOR GNU/LINUX." - "\nTHIS UTILITY WILL IRRECOVERABLY WIPE DATA FROM DRIVE.\nPROCEED WITH CAUTION.") - - wipe_device() diff --git a/src/pywype/command_line.py b/src/pywype/command_line.py new file mode 100644 index 0000000..b026b09 --- /dev/null +++ b/src/pywype/command_line.py @@ -0,0 +1,15 @@ +import pywype + + +def main(): + print(28 * "-", " pyWype ", 28 * "-") + print( + "PYTHON DISK & PARTITION WIPING UTILITY FOR GNU/LINUX." + "\nTHIS UTILITY WILL IRRECOVERABLY WIPE DATA FROM DRIVE.\nPROCEED WITH CAUTION." + ) + + pywype.wipe_device() + + +if __name__ == "__main__": + main() diff --git a/tests/test_is_linux.py b/tests/test_is_linux.py index f7e53ce..13d7786 100644 --- a/tests/test_is_linux.py +++ b/tests/test_is_linux.py @@ -8,6 +8,7 @@ import unittest from unittest.mock import patch + def is_linux(): """Check if system is 'Linux' """ @@ -15,6 +16,9 @@ def is_linux(): print("This program was designed for GNU/Linux. Exiting.") sys.exit() """ + + + class CheckOS(unittest.TestCase): #Unit test.