Skip to content

Commit

Permalink
Merge pull request #35 from mkcn/clipboard-feature
Browse files Browse the repository at this point in the history
Clipboard feature
  • Loading branch information
mkcn committed Mar 8, 2021
2 parents c8ea3b9 + 89156b3 commit d531d41
Show file tree
Hide file tree
Showing 7 changed files with 109 additions and 75 deletions.
9 changes: 6 additions & 3 deletions .github/workflows/pythonUnitTest.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ jobs:
build:
strategy:
matrix:
os: [ubuntu-16.04, ubuntu-18.04, macos-10.15] # ubuntu-16.04, macos-10.15
python-version: [3.6, 3.7, 3.8] # 3.7, 3.8
os: [ubuntu-16.04, ubuntu-latest, macos-latest]
python-version: [3.6, 3.8]

runs-on: ${{ matrix.os }}

Expand All @@ -27,7 +27,8 @@ jobs:
if [[ "$OSTYPE" == "linux-gnu" ]]; then
echo "OS: Linux"
sudo apt-get install nmap # nmap is needed for the man parser in UnitTest
sudo apt-get --yes install net-tools nmap # needed for the man parser in UnitTest
# sudo apt-get --yes install xclip # needed for pyperclip, only on Linux (more info: https://pyperclip.readthedocs.io/en/latest/index.html#not-implemented-error)
elif [[ "$OSTYPE" == "darwin"* ]]; then
echo "OS: Mac OSX"
brew install nmap
Expand All @@ -40,6 +41,8 @@ jobs:
if: always()
run: |
$GITHUB_WORKSPACE/installer.sh
# this flag file is needed to run the TestMain unitTests (test_main.py)
touch $HOME/.local/share/fastHistory/THIS_IS_A_TEST_FASTHISTORY_INSTALLATION
- name: Test with UnitTest
run: |
Expand Down
61 changes: 21 additions & 40 deletions fastHistory/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,26 +36,16 @@ def handle_search_request(logger_console, input_cmd_str, path_data_folder, theme
picker = Picker(data_manager, theme=theme, last_column_size=last_column_size, search_text=input_cmd_str)
selected_option = picker.start()

# inject into the terminal the selected command
try:
if selected_option[0]:
ConsoleUtils.fill_terminal_input(selected_option[1])
if selected_option[0]:
res = ConsoleUtils.paste_into_terminal(selected_option[1])
if not res[0]:
logger_console.log_on_console_error(res[1])
else:
res = ConsoleUtils.copy_to_clipboard(selected_option[1])
if res[0]:
logger_console.log_on_console_info(res[1])
else:
if ConsoleUtils.set_value_clipboard(selected_option[1]):
logger_console.log_on_console_info("copied to clipboard: '%s'" % selected_option[1])
else:
logger_console.log_on_console_error("pyperclip package not found")
logger_console.log_on_console_error("To enable auto-copy execute the following command:")
logger_console.log_on_console("")
logger_console.log_on_console("pip3 install pyperclip")
logger_console.log_on_console("")
except:
logging.debug("your terminal does not support automatic input injection")
logger_console.log_on_console_error("your terminal does not support automatic input injection")
logger_console.log_on_console_error("please manually copy and paste the selected command")
logger_console.log_on_console("")
logger_console.log_on_console(selected_option)
logger_console.log_on_console("")
logger_console.log_on_console_error(res[1])


def handle_add_request(logger_console, input_cmd_str, path_data_folder, error_feedback=False):
Expand Down Expand Up @@ -192,38 +182,29 @@ def handle_update(logger_console):
update_command = update_with_installer
except ImportError as e:
update_command = update_with_installer
logger_console.log_on_console_info("to update fastHistory use the following injected command")
try:
ConsoleUtils.fill_terminal_input(update_command)
except:
logger_console.log_on_console_error("your terminal does not support automatic input injection")
logger_console.log_on_console_error("please copy and execute the following command")
logger_console.log_on_console(update_command)
logger_console.log_on_console_info("to update fastHistory use the following command")
res = ConsoleUtils.paste_into_terminal(update_command)
if not res[0]:
logger_console.log_on_console_error(res[1])


def handle_config_file(logger_console, path_data_folder):
config_file = path_data_folder + NAME_CONFIGURATION_FILE
logger_console.log_on_console_info("to change the config file use the following injected command")
try:
ConsoleUtils.fill_terminal_input("nano " + config_file)
except:
logger_console.log_on_console_error("your terminal does not support automatic input injection")
logger_console.log_on_console_error("please edit the configuration file manually")
logger_console.log_on_console(config_file)
logger_console.log_on_console_info("to change the config file use the following command")
res = ConsoleUtils.paste_into_terminal("nano " + config_file)
if not res[0]:
logger_console.log_on_console_error(res[1])


def handle_log_file(logger_console, path_data_folder):
log_file = path_data_folder + NAME_LOG_FILE
if not os.path.exists(log_file):
logger_console.log_on_console_warn("log file not found, try to change the log level in the config file")
else:
logger_console.log_on_console_info("to read the log file use the following injected command")
try:
ConsoleUtils.fill_terminal_input("nano " + log_file)
except:
logger_console.log_on_console_error("your terminal does not support automatic input injection")
logger_console.log_on_console_error("please read the log file manually")
logger_console.log_on_console(log_file)
logger_console.log_on_console_info("to read the log file use the following command")
res = ConsoleUtils.paste_into_terminal("nano " + log_file)
if not res[0]:
logger_console.log_on_console_error(res[1])


def handle_helper(logger_console):
Expand Down
2 changes: 1 addition & 1 deletion fastHistory/config/default_version.txt
Original file line number Diff line number Diff line change
@@ -1 +1 @@
2.2.1
2.2.2
39 changes: 25 additions & 14 deletions fastHistory/console/consoleUtils.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,31 +15,42 @@ class ConsoleUtils:
"""

@staticmethod
def fill_terminal_input(data):
def paste_into_terminal(data):
"""
Fill terminal input with data
# https://unix.stackexchange.com/a/217390
"""
# check if python version >= 3
if sys.version_info >= (3,):
# reverse the automatic encoding and pack into a list of bytes
data = (struct.pack('B', c) for c in os.fsencode(data))

# put each char of data in the standard input of the current terminal
for c in data:
fcntl.ioctl(sys.stdin, termios.TIOCSTI, c)
# clear output printed by the previous command
# and leave only the terminal with the submitted input
sys.stdout.write('\r')
try:
# check if python version >= 3
if sys.version_info >= (3,):
# reverse the automatic encoding and pack into a list of bytes
data_bytes = (struct.pack('B', c) for c in os.fsencode(data))

# put each char of data in the standard input of the current terminal
for c in data_bytes:
fcntl.ioctl(sys.stdin, termios.TIOCSTI, c)
# clear output printed by the previous command
# and leave only the terminal with the submitted input
sys.stdout.write('\r')
return [True, None]
except Exception:
res = ConsoleUtils.copy_to_clipboard(data)
if res[0]:
return [False, "your terminal does not support auto-paste, the command is copied to clipboard instead:\n%s" % data]
else:
return [False, "your terminal does not support auto-paste\ncopy-to-clipboard failed too with the following message:\n\t%s\nplease manually copy and use the following command:\n\t%s" % (res[1], data)]

@staticmethod
def set_value_clipboard(data):
def copy_to_clipboard(data):
try:
import pyperclip
pyperclip.copy(data)
return True
return [True, "copied to clipboard: %s" % data]
except ImportError:
return [False, "pyperclip module not found (to install it run 'pip3 install pyperclip')"]
except Exception as e:
return False
return [False, "pyperclip error: %s" % str(e)]

@staticmethod
def handler_close_signal(signum, frame):
Expand Down
53 changes: 38 additions & 15 deletions fastHistory/unitTests/test_main.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,22 +5,35 @@
import fastHistory
import sys

from fastHistory import ConsoleUtils
from fastHistory.console import colors
from fastHistory.unitTests.loggerTest import LoggerBashTest, LoggerTest


class TestMain(unittest.TestCase):
"""
note: some of these tests need a successful installation and setup of fastHistory
included bashlex, pyperclip modules
"""

NAME_FILE_THIS_IS_A_TEST_FASTHISTORY_INSTALLATION = "THIS_IS_A_TEST_FASTHISTORY_INSTALLATION"

@classmethod
def setUpClass(cls):
cls.logger_test = LoggerTest()
cls.output_file = cls.logger_test.get_test_folder() + "output.db"
cls.path_data_folder = ConsoleUtils.compose_home_relative_path(fastHistory.PATH_DATA_FOLDER)

def setUp(self):
self.logger_test.log_test_function_name(self.id())
self.assertTrue(os.path.isfile(self.path_data_folder + fastHistory.NAME_CONFIGURATION_FILE),
"To run these tests you first need to install fastHistory on this machine:\n./installer.sh")
self.assertTrue(os.path.isfile(self.path_data_folder + TestMain.NAME_FILE_THIS_IS_A_TEST_FASTHISTORY_INSTALLATION),
"A fastHistory installation has been found,\n\
these tests should NOT be run with your daily used fastHistory but only in a test environment\n\
if you understand this, please manually create a file with the following command:\ntouch %s\n\
otherwise just ignore these test cases"
% (self.path_data_folder + TestMain.NAME_FILE_THIS_IS_A_TEST_FASTHISTORY_INSTALLATION))

if os.path.isfile(self.output_file):
os.remove(self.output_file)
Expand Down Expand Up @@ -104,7 +117,6 @@ def test_call_add_error(self):
self.assertEqual(len(console_logs), 0)

def test_call_export_with_parameter_and_import(self):

logger_test = LoggerBashTest()
sys.argv = ["", "--export", self.output_file, "--from-installer"]
fastHistory.f(logger_console=logger_test)
Expand Down Expand Up @@ -138,7 +150,7 @@ def test_call_import_with_error(self):
console_logs = logger_test.get_console_logs()
self.assertRegex(console_logs[1][LoggerBashTest.INDEX_VALUE], "^input file does not exist:")

def test_call_setup(self):
def __test_call_setup(self):
logger_test = LoggerBashTest()
sys.argv = ["", "--setup", "--from-installer"]
fastHistory.f(logger_console=logger_test)
Expand All @@ -149,7 +161,7 @@ def test_call_setup(self):
# last message value
self.assertRegex(console_logs[-1][LoggerBashTest.INDEX_VALUE], "^setup completed")

def test_call_setup_with_config_reader_error(self):
def __test_call_setup_with_config_reader_error(self):
logger_test = LoggerBashTest()
sys.argv = ["", "--setup", ]
fastHistory.f(logger_console=logger_test)
Expand All @@ -166,13 +178,16 @@ def test_call_update(self):
fastHistory.f(logger_console=logger_test)
console_logs = logger_test.get_console_logs()
# last message value
if len(console_logs) == 4:
if len(console_logs) == 2:
self.assertEqual(console_logs[0][LoggerBashTest.INDEX_TYPE], LoggerBashTest.INFO)
self.assertEqual(console_logs[0][LoggerBashTest.INDEX_VALUE], "to update fastHistory use the following command")
self.assertEqual(console_logs[1][LoggerBashTest.INDEX_TYPE], LoggerBashTest.ERROR)
self.assertEqual(console_logs[1][LoggerBashTest.INDEX_VALUE], "your terminal does not support automatic input injection")
self.assertRegex(console_logs[1][LoggerBashTest.INDEX_VALUE], "^your terminal does not support auto-paste")
elif len(console_logs) == 1:
# note: this test may be successful if executed directly from the terminal
# e.g. python3 -m unittest discover -s fastHistory/
self.assertEqual(console_logs[0][LoggerBashTest.INDEX_VALUE], "to update fastHistory use the following injected command")
self.assertEqual(console_logs[0][LoggerBashTest.INDEX_TYPE], LoggerBashTest.INFO)
self.assertEqual(console_logs[0][LoggerBashTest.INDEX_VALUE], "to update fastHistory use the following command")
else:
self.assertTrue(False)

Expand All @@ -182,15 +197,17 @@ def test_call_log_with_error(self):
fastHistory.f(logger_console=logger_test)
console_logs = logger_test.get_console_logs()
# last message value
if len(console_logs) == 4:
if len(console_logs) == 2:
self.assertEqual(console_logs[0][LoggerBashTest.INDEX_TYPE], LoggerBashTest.INFO)
self.assertEqual(console_logs[0][LoggerBashTest.INDEX_VALUE], "to read the log file use the following command")
self.assertEqual(console_logs[1][LoggerBashTest.INDEX_TYPE], LoggerBashTest.ERROR)
self.assertEqual(console_logs[1][LoggerBashTest.INDEX_VALUE], "your terminal does not support automatic input injection")
self.assertRegex(console_logs[1][LoggerBashTest.INDEX_VALUE], "^your terminal does not support auto-paste")
elif len(console_logs) == 1:
# note: this test may be successful if executed directly from the terminal
# e.g. python3 -m unittest discover -s fastHistory/
if console_logs[0][LoggerBashTest.INDEX_VALUE] == "log file not found, try to change the log level in the config file":
self.assertTrue(True)
elif console_logs[0][LoggerBashTest.INDEX_VALUE] == "to read the log file use the following injected command":
elif console_logs[0][LoggerBashTest.INDEX_VALUE] == "to read the log file use the following command":
self.assertTrue(True)
else:
self.assertTrue(False)
Expand All @@ -203,13 +220,16 @@ def test_call_config_with_error(self):
fastHistory.f(logger_console=logger_test)
console_logs = logger_test.get_console_logs()
# last message value
if len(console_logs) == 4:
if len(console_logs) == 2:
self.assertEqual(console_logs[0][LoggerBashTest.INDEX_TYPE], LoggerBashTest.INFO)
self.assertEqual(console_logs[0][LoggerBashTest.INDEX_VALUE], "to change the config file use the following command")
self.assertEqual(console_logs[1][LoggerBashTest.INDEX_TYPE], LoggerBashTest.ERROR)
self.assertEqual(console_logs[1][LoggerBashTest.INDEX_VALUE], "your terminal does not support automatic input injection")
self.assertRegex(console_logs[1][LoggerBashTest.INDEX_VALUE], "^your terminal does not support auto-paste")
elif len(console_logs) == 1:
# note: this test may be successful if executed directly from the terminal
# e.g. python3 -m unittest discover -s fastHistory/
self.assertEqual(console_logs[0][LoggerBashTest.INDEX_VALUE], "to change the config file use the following injected command")
self.assertEqual(console_logs[0][LoggerBashTest.INDEX_TYPE], LoggerBashTest.INFO)
self.assertEqual(console_logs[0][LoggerBashTest.INDEX_VALUE], "to change the config file use the following command")
else:
self.assertTrue(False)

Expand All @@ -219,12 +239,15 @@ def test_call_config_with_error_with_config_reader_error(self):
fastHistory.f(logger_console=logger_test)
console_logs = logger_test.get_console_logs()
# last message value
if len(console_logs) == 4:
if len(console_logs) == 2:
self.assertEqual(console_logs[0][LoggerBashTest.INDEX_TYPE], LoggerBashTest.INFO)
self.assertEqual(console_logs[0][LoggerBashTest.INDEX_VALUE], "to change the config file use the following command")
self.assertEqual(console_logs[1][LoggerBashTest.INDEX_TYPE], LoggerBashTest.ERROR)
self.assertEqual(console_logs[1][LoggerBashTest.INDEX_VALUE], "your terminal does not support automatic input injection")
self.assertRegex(console_logs[1][LoggerBashTest.INDEX_VALUE], "^your terminal does not support auto-paste")
elif len(console_logs) == 1:
# note: this test may be successful if executed directly from the terminal
# e.g. python3 -m unittest discover -s fastHistory/
self.assertEqual(console_logs[0][LoggerBashTest.INDEX_VALUE], "to change the config file use the following injected command")
self.assertEqual(console_logs[0][LoggerBashTest.INDEX_TYPE], LoggerBashTest.INFO)
self.assertEqual(console_logs[0][LoggerBashTest.INDEX_VALUE], "to change the config file use the following command")
else:
self.assertTrue(False)
7 changes: 5 additions & 2 deletions pip/build.sh
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,16 @@ installation_python_env="build-env/bin/activate"
version_file="../fastHistory/config/default_version.txt"


# note: manually create $HOME/.pypirc based on the pyirc_template
# https://pypi.org/manage/account/token/
# https://test.pypi.org/manage/account/token/
pypi_upload=false
pypi_domain="pypi.org"
pypi_repo="pypi" #defined in the $HOME/.pypirc file
pypi_repo="pypi"

pypi_test_upload=false
pypi_test_domain="test.pypi.org"
pypi_test_repo="testpypi" #defined in the $HOME/.pypirc file
pypi_test_repo="testpypi"


if [ -f setup.py ]; then
Expand Down
13 changes: 13 additions & 0 deletions pip/pypirc_template
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
[distutils]
index-servers=
pypi
testpypi

[pypi]
username: __token__
password: pypi-...

[testpypi]
repository: https://test.pypi.org/legacy/
username: __token__
password: pypi-...

0 comments on commit d531d41

Please sign in to comment.