diff options
author | Amjith Ramanujam <amjith.r@gmail.com> | 2019-05-06 06:35:13 -0700 |
---|---|---|
committer | Amjith Ramanujam <amjith.r@gmail.com> | 2019-05-06 07:18:46 -0700 |
commit | a6d44df4a7eb090708259a1ed73f2729e48b1e92 (patch) | |
tree | be6aecf439786ab08a01387bf58dba03f16afefc | |
parent | e5501cb83c47d4e616cc5a432991f6fbf74a36e5 (diff) | |
parent | 30c788917bf083aae6c4331ee102f8785c69432a (diff) |
Merge branch 'master' into pr1035
-rw-r--r-- | .travis.yml | 8 | ||||
-rw-r--r-- | AUTHORS | 3 | ||||
-rw-r--r-- | changelog.rst | 20 | ||||
-rw-r--r-- | pgcli/key_bindings.py | 4 | ||||
-rw-r--r-- | pgcli/main.py | 38 | ||||
-rw-r--r-- | pgcli/pgexecute.py | 7 | ||||
-rw-r--r-- | setup.py | 1 | ||||
-rw-r--r-- | tests/test_main.py | 10 | ||||
-rw-r--r-- | tox.ini | 11 |
9 files changed, 76 insertions, 26 deletions
diff --git a/.travis.yml b/.travis.yml index 04e570f2..2bb738e5 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,9 +1,15 @@ +dist: xenial + +sudo: required + language: python + python: - "2.7" - "3.4" - "3.5" - "3.6" + - "3.7" before_install: - which python @@ -43,4 +49,4 @@ services: - postgresql addons: - postgresql: "9.3" + postgresql: "9.6" @@ -91,6 +91,9 @@ Contributors: * Marcin Cieślak (saper) * easteregg (verfriemelt-dot-org) * Scott Brenstuhl (808sAndBR) + * Nathan Verzemnieks + * raylu + * Zhaolong Zhu Creator: -------- diff --git a/changelog.rst b/changelog.rst index 2c1324ef..1546ff8d 100644 --- a/changelog.rst +++ b/changelog.rst @@ -1,3 +1,19 @@ +Upcoming: +========= + +Bug fixes: +---------- +* Escape switches to VI navigation mode when not canceling completion popup. (Thanks: `Nathan Verzemnieks`_) +* Allow application_name to be overridden. (Thanks: `raylu`_) +* Fix for "no attribute KeyringLocked" (#1040). (Thanks: `Irina Truong`_) +* Pgcli no longer works with password containing spaces (#1043). (Thanks: `Irina Truong`_) +* Load keyring only when keyring is enabled in the config file (#1041). (Thanks: `Zhaolong Zhu`_) + +Internal: +--------- + +* Add python 3.7 to travis build matrix. (Thanks: `Irina Truong`_) + 2.1.0 ===== @@ -950,3 +966,7 @@ Improvements: .. _`Marcin Cieślak`: https://github.com/saper .. _`Scott Brenstuhl`: https://github.com/808sAndBR .. _`easteregg`: https://github.com/verfriemelt-dot-org +.. _`Nathan Verzemnieks`: https://github.com/njvrzm +.. _`raylu`: https://github.com/raylu +.. _`Zhaolong Zhu`: https://github.com/zzl0 + diff --git a/pgcli/key_bindings.py b/pgcli/key_bindings.py index dbb4035c..d7750432 100644 --- a/pgcli/key_bindings.py +++ b/pgcli/key_bindings.py @@ -3,7 +3,7 @@ from __future__ import unicode_literals import logging from prompt_toolkit.enums import EditingMode from prompt_toolkit.key_binding import KeyBindings -from prompt_toolkit.filters import completion_is_selected +from prompt_toolkit.filters import completion_is_selected, has_completions _logger = logging.getLogger(__name__) @@ -50,7 +50,7 @@ def pgcli_bindings(pgcli): else: buff.insert_text(tab_insert_text, fire_event=False) - @kb.add('escape') + @kb.add('escape', filter=has_completions) def _(event): """Force closing of autocompletion.""" _logger.debug('Detected <Esc> key.') diff --git a/pgcli/main.py b/pgcli/main.py index d7853e47..865c4f3a 100644 --- a/pgcli/main.py +++ b/pgcli/main.py @@ -20,7 +20,7 @@ import datetime as dt import itertools from time import time, sleep from codecs import open - +keyring = None # keyring will be loaded later from cli_helpers.tabular_output import TabularOutputFormatter from cli_helpers.tabular_output.preprocessors import (align_decimals, @@ -45,10 +45,7 @@ from pygments.lexers.sql import PostgresLexer from pgspecial.main import (PGSpecial, NO_QUERY, PAGER_OFF, PAGER_LONG_OUTPUT) import pgspecial as special -try: - import keyring -except ImportError: - keyring = None + from .pgcompleter import PGCompleter from .pgtoolbar import create_toolbar_tokens_func from .pgstyle import style_factory, style_factory_output @@ -186,7 +183,7 @@ class PGCli(object): self.on_error = c['main']['on_error'].upper() self.decimal_format = c['data_formats']['decimal'] self.float_format = c['data_formats']['float'] - self.keyring_enabled = c["main"].as_bool("keyring") + self.initialize_keyring() self.pgspecial.pset_pager(self.config['main'].as_bool( 'enable_pager') and "on" or "off") @@ -293,7 +290,7 @@ class PGCli(object): db, user, host, port = infos try: self.pgexecute.connect(database=db, user=user, host=host, - port=port, application_name='pgcli') + port=port, **self.pgexecute.extra_args) except OperationalError as e: click.secho(str(e), err=True, fg='red') click.echo("Previous connection kept") @@ -382,6 +379,18 @@ class PGCli(object): pgspecial_logger.addHandler(handler) pgspecial_logger.setLevel(log_level) + def initialize_keyring(self): + global keyring + + keyring_enabled = self.config["main"].as_bool("keyring") + if keyring_enabled: + # Try best to load keyring (issue #1041). + import importlib + try: + keyring = importlib.import_module('keyring') + except Exception as e: # ImportError for Python 2, ModuleNotFoundError for Python 3 + self.logger.warning('import keyring failed: %r.', e) + def connect_dsn(self, dsn): self.connect(dsn=dsn) @@ -404,6 +413,8 @@ class PGCli(object): if not database: database = user + kwargs.setdefault('application_name', 'pgcli') + # If password prompt is not forced but no password is provided, try # getting it from environment variable. if not self.force_passwd_prompt and not passwd: @@ -418,7 +429,8 @@ class PGCli(object): - prepare keyring as described at: https://keyring.readthedocs.io/en/stable/ - uninstall keyring: pip uninstall keyring - disable keyring in our configuration: add keyring = False to [main]""") - if not passwd and keyring and self.keyring_enabled: + if not passwd and keyring: + try: passwd = keyring.get_password('pgcli', key) except ( @@ -462,24 +474,22 @@ class PGCli(object): try: try: pgexecute = PGExecute(database, user, passwd, host, port, dsn, - application_name='pgcli', **kwargs) + **kwargs) except (OperationalError, InterfaceError) as e: if should_ask_for_password(e): passwd = click.prompt('Password for %s' % user, hide_input=True, show_default=False, type=str) pgexecute = PGExecute(database, user, passwd, host, port, - dsn, application_name='pgcli', - **kwargs) + dsn, **kwargs) else: raise e - if passwd and keyring and self.keyring_enabled: + if passwd and keyring: try: keyring.set_password('pgcli', key, passwd) except ( - keyring.errors.InitError, RuntimeError, - keyring.errors.KeyringLocked + keyring.errors.KeyringError, ) as e: click.secho( keyring_error_message.format( diff --git a/pgcli/pgexecute.py b/pgcli/pgexecute.py index 3f78989a..93c8de42 100644 --- a/pgcli/pgexecute.py +++ b/pgcli/pgexecute.py @@ -7,7 +7,7 @@ import psycopg2.extensions as ext import sqlparse import pgspecial as special import select -from psycopg2.extensions import POLL_OK, POLL_READ, POLL_WRITE +from psycopg2.extensions import POLL_OK, POLL_READ, POLL_WRITE, make_dsn from .packages.parseutils.meta import FunctionMetadata, ForeignKey from .encodingutils import unicode2utf8, PY2, utf8tounicode @@ -199,6 +199,7 @@ class PGExecute(object): self.host = None self.port = None self.server_version = None + self.extra_args = None self.connect(database, user, password, host, port, dsn, **kwargs) def copy(self): @@ -233,13 +234,13 @@ class PGExecute(object): } new_params.update(kwargs) - if new_params['dsn'] is not None: + if new_params['dsn']: new_params = { 'dsn': new_params['dsn'], 'password': new_params['password'] } - if new_params['password'] is not None: + if new_params['password']: new_params['dsn'] = "{0} password={1}".format( new_params['dsn'], new_params.pop('password') ) @@ -62,6 +62,7 @@ setup( 'Programming Language :: Python :: 3.4', 'Programming Language :: Python :: 3.5', 'Programming Language :: Python :: 3.6', + 'Programming Language :: Python :: 3.7', 'Programming Language :: SQL', 'Topic :: Database', 'Topic :: Database :: Front-Ends', diff --git a/tests/test_main.py b/tests/test_main.py index b1c40356..e8e801d5 100644 --- a/tests/test_main.py +++ b/tests/test_main.py @@ -13,6 +13,7 @@ except ImportError: from pgcli.main import ( obfuscate_process_password, format_output, PGCli, OutputSettings, COLOR_CODE_REGEX ) +from pgcli.pgexecute import PGExecute from pgspecial.main import (PAGER_OFF, PAGER_LONG_OUTPUT, PAGER_ALWAYS) from utils import dbtest, run from collections import namedtuple @@ -309,3 +310,12 @@ def test_multihost_db_uri(tmpdir): user='bar', passwd='foo', port='2543,2543,2543') + + +def test_application_name_db_uri(tmpdir): + with mock.patch.object(PGExecute, '__init__') as mock_pgexecute: + mock_pgexecute.return_value = None + cli = PGCli(pgclirc_file=str(tmpdir.join("rcfile"))) + cli.connect_uri('postgres://bar@baz.com/?application_name=cow') + mock_pgexecute.assert_called_with('bar', 'bar', '', 'baz.com', '', '', + application_name='cow') @@ -1,11 +1,10 @@ [tox] -envlist = py27, py34, py35, py36 +envlist = py27, py34, py35, py36, py37 [testenv] -deps = pytest - mock - pgspecial - humanize - psycopg2 +deps = pytest>=2.7.0,<=3.0.7 + mock>=1.0.1 + behave>=1.2.4 + pexpect==3.3 commands = py.test behave tests/features passenv = PGHOST |