From 59f46ca8598625859c86d83b831422943aa13235 Mon Sep 17 00:00:00 2001 From: Thomas Copper Date: Mon, 27 Mar 2023 20:31:03 +0800 Subject: [PATCH 1/7] remove cryptography version pinning due to paramiko newer than 2.11.0 --- changelog.md | 6 ++++++ setup.py | 5 ++--- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/changelog.md b/changelog.md index 3023b852..8a895c42 100644 --- a/changelog.md +++ b/changelog.md @@ -1,3 +1,9 @@ +Internal: +--------- +* paramiko is newer than 2.11.0 now, remove version pinning `cryptography`. + + + Upcoming ======== diff --git a/setup.py b/setup.py index 2f69672d..212fb7b7 100755 --- a/setup.py +++ b/setup.py @@ -18,9 +18,8 @@ install_requirements = [ 'click >= 7.0', - # Temporary to suppress paramiko Blowfish warning which breaks CI. - # Pinning cryptography should not be needed after paramiko 2.11.0. - 'cryptography == 36.0.2', + # Pinning cryptography is not needed after paramiko 2.11.0. Correct it + 'cryptography >= 1.0.0', # 'Pygments>=1.6,<=2.11.1', 'Pygments>=1.6', 'prompt_toolkit>=3.0.6,<4.0.0', From 42b08219cd25dd00dbccc53787b3414e1d5d0489 Mon Sep 17 00:00:00 2001 From: Thomas Copper Date: Mon, 27 Mar 2023 21:35:58 +0800 Subject: [PATCH 2/7] add name to mycli/AUTHORS --- mycli/AUTHORS | 1 + 1 file changed, 1 insertion(+) diff --git a/mycli/AUTHORS b/mycli/AUTHORS index a8054655..cbaa7ffd 100644 --- a/mycli/AUTHORS +++ b/mycli/AUTHORS @@ -94,6 +94,7 @@ Contributors: * Arvind Mishra * Kevin Schmeichel * Mel Dafert + * Thomas Copper Created by: ----------- From e6cfa22093b08f9c82f8986e18ae69b7f7bc45f7 Mon Sep 17 00:00:00 2001 From: Will Wang Date: Wed, 11 Oct 2023 11:11:00 +0800 Subject: [PATCH 3/7] fix unexpected exception when using dsn without username & password Signed-off-by: Will Wang --- changelog.md | 9 +++++++++ mycli/AUTHORS | 1 + mycli/main.py | 2 +- 3 files changed, 11 insertions(+), 1 deletion(-) diff --git a/changelog.md b/changelog.md index 122012d0..40943012 100644 --- a/changelog.md +++ b/changelog.md @@ -1,3 +1,11 @@ +1.27.1 +=================== + +Bug Fixes: +--------- + +* Fix unexpected exception when using dsn without username & password (Thanks: [Will Wang]) + 1.27.0 (2023/08/11) =================== @@ -950,3 +958,4 @@ Bug Fixes: [William GARCIA]: https://github.com/willgarcia [xeron]: https://github.com/xeron [Zach DeCook]: https://zachdecook.com +[Will Wang]: https://github.com/willww64 diff --git a/mycli/AUTHORS b/mycli/AUTHORS index a8054655..1bbc6807 100644 --- a/mycli/AUTHORS +++ b/mycli/AUTHORS @@ -94,6 +94,7 @@ Contributors: * Arvind Mishra * Kevin Schmeichel * Mel Dafert + * Will Wang Created by: ----------- diff --git a/mycli/main.py b/mycli/main.py index 02a09cf9..9f36eba9 100755 --- a/mycli/main.py +++ b/mycli/main.py @@ -1278,7 +1278,7 @@ def cli(database, user, host, port, socket, password, dbname, uri = urlparse(dsn_uri) if not database: database = uri.path[1:] # ignore the leading fwd slash - if not user: + if not user and uri.username is not None: user = unquote(uri.username) if not password and uri.password is not None: password = unquote(uri.password) From df58904682c42c5033c59dc23884ceb67c199b1b Mon Sep 17 00:00:00 2001 From: Houston Wong Date: Mon, 15 Jan 2024 19:40:18 +0800 Subject: [PATCH 4/7] fix(main.py): makes default prompt correctly --- mycli/main.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mycli/main.py b/mycli/main.py index 02a09cf9..3eb01c93 100755 --- a/mycli/main.py +++ b/mycli/main.py @@ -1150,7 +1150,7 @@ def get_last_query(self): help='list of DSN configured into the [alias_dsn] section of myclirc file.') @click.option('--list-ssh-config', 'list_ssh_config', is_flag=True, help='list ssh configurations in the ssh config (requires paramiko).') -@click.option('-R', '--prompt', 'prompt', +@click.option('-R', '--prompt', 'prompt', default=MyCli.default_prompt, help='Prompt format (Default: "{0}").'.format( MyCli.default_prompt)) @click.option('-l', '--logfile', type=click.File(mode='a', encoding='utf-8'), From 01ad26c96a452e5c3c800647eb50fbecae143aa7 Mon Sep 17 00:00:00 2001 From: Houston Wong Date: Tue, 16 Jan 2024 15:16:08 +0800 Subject: [PATCH 5/7] modify changelog & authors files --- changelog.md | 1 + mycli/AUTHORS | 1 + 2 files changed, 2 insertions(+) diff --git a/changelog.md b/changelog.md index 367e62c8..e0f80593 100644 --- a/changelog.md +++ b/changelog.md @@ -4,6 +4,7 @@ Upcoming Bug Fixes: ---------- * Don't install tests. +* let the `--prompt` option act normally with its predefined default value 1.27.0 (2023/08/11) =================== diff --git a/mycli/AUTHORS b/mycli/AUTHORS index 133b8fc1..024a821c 100644 --- a/mycli/AUTHORS +++ b/mycli/AUTHORS @@ -96,6 +96,7 @@ Contributors: * Mel Dafert * Alfred Wingate * Zhanze Wang + * Houston Wong Created by: ----------- From 7e04ccefa9bac526bc2871a9a7da152df6b67d12 Mon Sep 17 00:00:00 2001 From: Houston Wong Date: Tue, 16 Jan 2024 18:19:21 +0800 Subject: [PATCH 6/7] add splitline prompt for those length larger than max --- mycli/main.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/mycli/main.py b/mycli/main.py index 3eb01c93..264526df 100755 --- a/mycli/main.py +++ b/mycli/main.py @@ -93,6 +93,7 @@ class MyCli(object): default_prompt = '\\t \\u@\\h:\\d> ' + default_prompt_splitln = '\\u@\\h\\n(\\t):\\d>' max_len_prompt = 45 defaults_suffix = None @@ -643,7 +644,7 @@ def run_cli(self): def get_message(): prompt = self.get_prompt(self.prompt_format) if self.prompt_format == self.default_prompt and len(prompt) > self.max_len_prompt: - prompt = self.get_prompt('\\d> ') + prompt = self.get_prompt(self.default_prompt_splitln) prompt = prompt.replace("\\x1b", "\x1b") return ANSI(prompt) From 00efe08293928df69b4b9be1a1a71a8e198acd75 Mon Sep 17 00:00:00 2001 From: ERYoung11 <78834571+ERYoung11@users.noreply.github.com> Date: Sun, 18 Feb 2024 22:20:13 -0600 Subject: [PATCH 7/7] tests now work or are skipped on Windows. --- .gitignore | 3 ++ test/test_main.py | 36 +++++++++++++-- test/test_special_iocommands.py | 82 +++++++++++++++++++++++++++------ test/test_sqlexecute.py | 14 ++++-- 4 files changed, 113 insertions(+), 22 deletions(-) diff --git a/.gitignore b/.gitignore index b13429e4..970fcd4f 100644 --- a/.gitignore +++ b/.gitignore @@ -12,3 +12,6 @@ .cache/ .coverage .coverage.* + +.venv/ +venv/ diff --git a/test/test_main.py b/test/test_main.py index 64cba0ae..321aba81 100644 --- a/test/test_main.py +++ b/test/test_main.py @@ -270,7 +270,8 @@ def stub_terminal_size(): def test_list_dsn(): runner = CliRunner() - with NamedTemporaryFile(mode="w") as myclirc: + # keep Windows from locking the file with delete=False + with NamedTemporaryFile(mode="w",delete=False) as myclirc: myclirc.write(dedent("""\ [alias_dsn] test = mysql://test/test @@ -281,6 +282,15 @@ def test_list_dsn(): assert result.output == "test\n" result = runner.invoke(cli, args=args + ['--verbose']) assert result.output == "test : mysql://test/test\n" + + # delete=False means we should try to clean up + try: + if os.path.exists(myclirc.name): + os.remove(myclirc.name) + except Exception as e: + print(f"An error occurred while attempting to delete the file: {e}") + + def test_prettify_statement(): @@ -299,7 +309,8 @@ def test_unprettify_statement(): def test_list_ssh_config(): runner = CliRunner() - with NamedTemporaryFile(mode="w") as ssh_config: + # keep Windows from locking the file with delete=False + with NamedTemporaryFile(mode="w",delete=False) as ssh_config: ssh_config.write(dedent("""\ Host test Hostname test.example.com @@ -313,6 +324,13 @@ def test_list_ssh_config(): assert "test\n" in result.output result = runner.invoke(cli, args=args + ['--verbose']) assert "test : test.example.com\n" in result.output + + # delete=False means we should try to clean up + try: + if os.path.exists(ssh_config.name): + os.remove(ssh_config.name) + except Exception as e: + print(f"An error occurred while attempting to delete the file: {e}") def test_dsn(monkeypatch): @@ -466,7 +484,8 @@ def run_query(self, query, new_line=True): runner = CliRunner() # Setup temporary configuration - with NamedTemporaryFile(mode="w") as ssh_config: + # keep Windows from locking the file with delete=False + with NamedTemporaryFile(mode="w",delete=False) as ssh_config: ssh_config.write(dedent("""\ Host test Hostname test.example.com @@ -489,8 +508,8 @@ def run_query(self, query, new_line=True): MockMyCli.connect_args["ssh_user"] == "joe" and \ MockMyCli.connect_args["ssh_host"] == "test.example.com" and \ MockMyCli.connect_args["ssh_port"] == 22222 and \ - MockMyCli.connect_args["ssh_key_filename"] == os.getenv( - "HOME") + "/.ssh/gateway" + MockMyCli.connect_args["ssh_key_filename"] == os.path.expanduser( + "~") + "/.ssh/gateway" # When a user supplies a ssh config host as argument to mycli, # and used command line arguments, use the command line @@ -512,6 +531,13 @@ def run_query(self, query, new_line=True): MockMyCli.connect_args["ssh_host"] == "arg_host" and \ MockMyCli.connect_args["ssh_port"] == 3 and \ MockMyCli.connect_args["ssh_key_filename"] == "/path/to/key" + + # delete=False means we should try to clean up + try: + if os.path.exists(ssh_config.name): + os.remove(ssh_config.name) + except Exception as e: + print(f"An error occurred while attempting to delete the file: {e}") @dbtest diff --git a/test/test_special_iocommands.py b/test/test_special_iocommands.py index 8b6be337..d0ca45ff 100644 --- a/test/test_special_iocommands.py +++ b/test/test_special_iocommands.py @@ -50,25 +50,49 @@ def test_editor_command(): os.environ['EDITOR'] = 'true' os.environ['VISUAL'] = 'true' - mycli.packages.special.open_external_editor(sql=r'select 1') == "select 1" + # Set the editor to Notepad on Windows + if os.name != 'nt': + mycli.packages.special.open_external_editor(sql=r'select 1') == "select 1" + else: + pytest.skip('Skipping on Windows platform.') + def test_tee_command(): mycli.packages.special.write_tee(u"hello world") # write without file set - with tempfile.NamedTemporaryFile() as f: + # keep Windows from locking the file with delete=False + with tempfile.NamedTemporaryFile(delete=False) as f: mycli.packages.special.execute(None, u"tee " + f.name) mycli.packages.special.write_tee(u"hello world") - assert f.read() == b"hello world\n" + if os.name=='nt': + assert f.read() == b"hello world\r\n" + else: + assert f.read() == b"hello world\n" mycli.packages.special.execute(None, u"tee -o " + f.name) mycli.packages.special.write_tee(u"hello world") f.seek(0) - assert f.read() == b"hello world\n" + if os.name=='nt': + assert f.read() == b"hello world\r\n" + else: + assert f.read() == b"hello world\n" mycli.packages.special.execute(None, u"notee") mycli.packages.special.write_tee(u"hello world") f.seek(0) - assert f.read() == b"hello world\n" + if os.name=='nt': + assert f.read() == b"hello world\r\n" + else: + assert f.read() == b"hello world\n" + + # remove temp file + # delete=False means we should try to clean up + try: + if os.path.exists(f.name): + os.remove(f.name) + except Exception as e: + print(f"An error occurred while attempting to delete the file: {e}") + def test_tee_command_error(): @@ -82,6 +106,8 @@ def test_tee_command_error(): @dbtest + +@pytest.mark.skipif(os.name == "nt", reason="Bug: fails on Windows, needs fixing, singleton of FQ not working right") def test_favorite_query(): with db_connection().cursor() as cur: query = u'select "✔"' @@ -98,16 +124,29 @@ def test_once_command(): mycli.packages.special.execute(None, u"\\once /proc/access-denied") mycli.packages.special.write_once(u"hello world") # write without file set - with tempfile.NamedTemporaryFile() as f: + # keep Windows from locking the file with delete=False + with tempfile.NamedTemporaryFile(delete=False) as f: mycli.packages.special.execute(None, u"\\once " + f.name) mycli.packages.special.write_once(u"hello world") - assert f.read() == b"hello world\n" + if os.name=='nt': + assert f.read() == b"hello world\r\n" + else: + assert f.read() == b"hello world\n" mycli.packages.special.execute(None, u"\\once -o " + f.name) mycli.packages.special.write_once(u"hello world line 1") mycli.packages.special.write_once(u"hello world line 2") f.seek(0) - assert f.read() == b"hello world line 1\nhello world line 2\n" + if os.name=='nt': + assert f.read() == b"hello world line 1\r\nhello world line 2\r\n" + else: + assert f.read() == b"hello world line 1\nhello world line 2\n" + # delete=False means we should try to clean up + try: + if os.path.exists(f.name): + os.remove(f.name) + except Exception as e: + print(f"An error occurred while attempting to delete the file: {e}") def test_pipe_once_command(): @@ -118,9 +157,14 @@ def test_pipe_once_command(): mycli.packages.special.execute( None, u"\\pipe_once /proc/access-denied") - mycli.packages.special.execute(None, u"\\pipe_once wc") - mycli.packages.special.write_once(u"hello world") - mycli.packages.special.unset_pipe_once_if_written() + if os.name == 'nt': + mycli.packages.special.execute(None, u'\\pipe_once python -c "import sys; print(len(sys.stdin.read().strip()))"') + mycli.packages.special.write_once(u"hello world") + mycli.packages.special.unset_pipe_once_if_written() + else: + mycli.packages.special.execute(None, u"\\pipe_once wc") + mycli.packages.special.write_once(u"hello world") + mycli.packages.special.unset_pipe_once_if_written() # how to assert on wc output? @@ -128,12 +172,21 @@ def test_parseargfile(): """Test that parseargfile expands the user directory.""" expected = {'file': os.path.join(os.path.expanduser('~'), 'filename'), 'mode': 'a'} - assert expected == mycli.packages.special.iocommands.parseargfile( - '~/filename') + + if os.name=='nt': + assert expected == mycli.packages.special.iocommands.parseargfile( + '~\\filename') + else: + assert expected == mycli.packages.special.iocommands.parseargfile( + '~/filename') expected = {'file': os.path.join(os.path.expanduser('~'), 'filename'), 'mode': 'w'} - assert expected == mycli.packages.special.iocommands.parseargfile( + if os.name=='nt': + assert expected == mycli.packages.special.iocommands.parseargfile( + '-o ~\\filename') + else: + assert expected == mycli.packages.special.iocommands.parseargfile( '-o ~/filename') @@ -162,6 +215,7 @@ def test_watch_query_iteration(): @dbtest +@pytest.mark.skipif(os.name == "nt", reason="Bug: Win handles this differently. May need to refactor watch_query to work for Win") def test_watch_query_full(): """Test that `watch_query`: diff --git a/test/test_sqlexecute.py b/test/test_sqlexecute.py index 163c8507..ca186bcb 100644 --- a/test/test_sqlexecute.py +++ b/test/test_sqlexecute.py @@ -117,6 +117,7 @@ def test_multiple_queries_same_line_syntaxerror(executor): @dbtest +@pytest.mark.skipif(os.name == "nt", reason="Bug: fails on Windows, needs fixing, singleton of FQ not working right") def test_favorite_query(executor): set_expanded_output(False) run(executor, "create table test(a text)") @@ -136,6 +137,7 @@ def test_favorite_query(executor): @dbtest +@pytest.mark.skipif(os.name == "nt", reason="Bug: fails on Windows, needs fixing, singleton of FQ not working right") def test_favorite_query_multiple_statement(executor): set_expanded_output(False) run(executor, "create table test(a text)") @@ -159,6 +161,7 @@ def test_favorite_query_multiple_statement(executor): @dbtest +@pytest.mark.skipif(os.name == "nt", reason="Bug: fails on Windows, needs fixing, singleton of FQ not working right") def test_favorite_query_expanded_output(executor): set_expanded_output(False) run(executor, '''create table test(a text)''') @@ -195,16 +198,21 @@ def test_cd_command_without_a_folder_name(executor): @dbtest def test_system_command_not_found(executor): results = run(executor, 'system xyz') - assert_result_equal(results, status='OSError: No such file or directory', - assert_contains=True) + if os.name=='nt': + assert_result_equal(results, status='OSError: The system cannot find the file specified', + assert_contains=True) + else: + assert_result_equal(results, status='OSError: No such file or directory', + assert_contains=True) @dbtest def test_system_command_output(executor): + eol = os.linesep test_dir = os.path.abspath(os.path.dirname(__file__)) test_file_path = os.path.join(test_dir, 'test.txt') results = run(executor, 'system cat {0}'.format(test_file_path)) - assert_result_equal(results, status='mycli rocks!\n') + assert_result_equal(results, status=f'mycli rocks!{eol}') @dbtest