summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAmjith Ramanujam <amjith.r@gmail.com>2019-05-06 06:35:13 -0700
committerAmjith Ramanujam <amjith.r@gmail.com>2019-05-06 07:18:46 -0700
commita6d44df4a7eb090708259a1ed73f2729e48b1e92 (patch)
treebe6aecf439786ab08a01387bf58dba03f16afefc
parente5501cb83c47d4e616cc5a432991f6fbf74a36e5 (diff)
parent30c788917bf083aae6c4331ee102f8785c69432a (diff)
Merge branch 'master' into pr1035
-rw-r--r--.travis.yml8
-rw-r--r--AUTHORS3
-rw-r--r--changelog.rst20
-rw-r--r--pgcli/key_bindings.py4
-rw-r--r--pgcli/main.py38
-rw-r--r--pgcli/pgexecute.py7
-rw-r--r--setup.py1
-rw-r--r--tests/test_main.py10
-rw-r--r--tox.ini11
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"
diff --git a/AUTHORS b/AUTHORS
index 28430c4b..63eb2c04 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -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')
)
diff --git a/setup.py b/setup.py
index 13c76c02..6c25b7a4 100644
--- a/setup.py
+++ b/setup.py
@@ -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')
diff --git a/tox.ini b/tox.ini
index 712db9cb..c8c75517 100644
--- a/tox.ini
+++ b/tox.ini
@@ -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