Skip to content
This repository has been archived by the owner on Nov 19, 2021. It is now read-only.

Commit

Permalink
Raise InvalidCredentialsError from C extension
Browse files Browse the repository at this point in the history
  • Loading branch information
ChrisRx committed Mar 14, 2017
1 parent 31691a6 commit 93b4094
Show file tree
Hide file tree
Showing 9 changed files with 69 additions and 231 deletions.
7 changes: 3 additions & 4 deletions giraffez/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
"""

__title__ = 'giraffez'
__version__ = '2.0.0-dev0'
__version__ = '2.0.0beta1'
__authors__ = ['Christopher Marshall', 'Kyle Travis']
__license__ = 'Apache 2.0'
__all__ = ['Export', 'MLoad', 'Load', 'Cmd', 'Config', 'Secret']
Expand All @@ -53,14 +53,13 @@
http://www.capitalone.io/giraffez/intro.html#environment.
""".format(error.msg))

from ._cli import TeradataError as TeradataCLIError
from ._tpt import TeradataError as TeradataPTError
from .cmd import TeradataCmd as Cmd
from .config import Config
from .constants import SILENCE, VERBOSE, DEBUG, INFO
from .errors import (
GeneralError,
GiraffeError,
MultiLoadError,
TeradataError,
GiraffeTypeError,
GiraffeEncodeError,
InvalidCredentialsError,
Expand Down
19 changes: 9 additions & 10 deletions giraffez/cmd.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,11 @@
# See the License for the specific language governing permissions and
# limitations under the License.

from ._cli import Cmd, RequestEnded, StatementEnded, TeradataError

from .constants import *
from .errors import *

from ._cli import Cmd, RequestEnded, StatementEnded, TeradataError

from .connection import Connection
from .fmt import truncate
from .logging import log
Expand Down Expand Up @@ -144,7 +144,6 @@ class TeradataCmd(Connection):
:param int log_level: Specify the desired level of output from the job.
Possible values are :code:`giraffez.SILENCE` :code:`giraffez.INFO` (default),
:code:`giraffez.VERBOSE` and :code:`giraffez.DEBUG`
:param bool panic: If :code:`True` stop executing commands and return after the first error.
:param str config: Specify an alternate configuration file to be read from, when previous paramaters are omitted.
:param str key_file: Specify an alternate key file to use for configuration decryption
:param string dsn: Specify a connection name from the configuration file to be
Expand All @@ -153,8 +152,7 @@ class TeradataCmd(Connection):
locks the connection used in the configuration file. This can be unlocked using the
command :code:`giraffez config --unlock <connection>` changing the connection password,
or via the :meth:`~giraffez.config.Config.unlock_connection` method.
:param string mload_session: Identifies mload sessions to suppress duplicate log output.
Used internally only.
:param string silent: Suppress log output. Used internally only.
:raises `giraffez.errors.InvalidCredentialsError`: if the supplied credentials are incorrect
:raises `giraffez.errors.TeradataError`: if the connection cannot be established
Expand Down Expand Up @@ -187,17 +185,18 @@ def close(self):
if getattr(self, 'cmd', None):
self.cmd.close()

def execute(self, command, sanitize=True, multi_statement=False, prepare_only=False,
silent=False, coerce_floats=True, parse_dates=False):
# TODO: Improve/fix this docstring
def execute(self, command, coerce_floats=True, parse_dates=False, multi_statement=False,
sanitize=True, silent=False, prepare_only=False):
"""
Execute commands using CLIv2.
:param str command: The SQL command to be executed
:param bool sanitize: Whether or not to call :func:`~giraffez.sql.prepare_statement` on the command
:param bool coerce_floats: Coerce Teradata decimal types into Python floats
:param bool parse_dates: Parses Teradata datetime types into Python datetimes
:param bool multi_statement: Execute in multi-statement mode
:param bool prepare_only: Only prepare the command (no results)
:param bool sanitize: Whether or not to call :func:`~giraffez.sql.prepare_statement` on the command
:param bool silent: Silence console logging (within this function only)
:param bool prepare_only: Only prepare the command (no results)
:return: the results of each statement in the command
:rtype: :class:`~giraffez.types.Rows`
:raises `giraffez.errors.TeradataError`: if the query is invalid
Expand Down
3 changes: 2 additions & 1 deletion giraffez/connection.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
from .constants import *
from .errors import *

from ._cli import InvalidCredentialsError
from .config import Config
from .logging import log
from .utils import show_warning, suppress_context
Expand Down Expand Up @@ -69,7 +70,7 @@ def __init__(self, host=None, username=None, password=None, log_level=INFO, conf
log.info("Connection", "Connection to '{}' established successfully.".format(self.dsn))
except InvalidCredentialsError as error:
if self.protect:
Config.lock_connection(self.config, self.dsn)
Config.lock_connection(self.config, self.dsn, self.key)
raise error

def _connect(self, host, username, password):
Expand Down
4 changes: 3 additions & 1 deletion giraffez/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@
from .constants import *
from .errors import *

from . import _cli
from . import _tpt
from .config import Config, message_write_default
from .encrypt import create_key_file
from .io import home_file
Expand Down Expand Up @@ -100,7 +102,7 @@ def run(self, test_args=None):
self.run()
else:
raise error
except InvalidCredentialsError as error:
except (_cli.InvalidCredentialsError, _tpt.InvalidCredentialsError) as error:
if args.protect:
Config.lock_connection(args.conf, args.dsn, args.key)
raise error
160 changes: 4 additions & 156 deletions giraffez/errors.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,52 +14,11 @@
# See the License for the specific language governing permissions and
# limitations under the License.

from .utils import suppress_context


class GiraffeError(Exception):
"""
Baseclass for all giraffez errors.
"""

class GeneralError(GiraffeError):
"""
General giraffez error.
"""

class GiraffeNotFound(GiraffeError):
"""
Raised when giraffez C modules not found.
"""

class TeradataCLIv2NotFound(GiraffeNotFound):
"""
Unable to import giraffez._cli. This indicates that either the
giraffez C extensions did not compile correctly, or more likely,
there is an issue with the environment or installation of the
Teradata dependencies. The Teradata Call-Level Interface Version 2
requires several environment variables to be set to find the shared
library files and error message catalog.
For more information, refer to this section in the giraffez
documentation:
http://www.capitalone.io/giraffez/intro.html#environment.
"""

class TeradataPTAPINotFound(GiraffeNotFound):
"""
Unable to import giraffez._tpt. This indicates that either the
giraffez C extensions did not compile correctly, or more likely,
there is an issue with the environment or installation of the
Teradata dependencies. The Teradata Parallel Transporter API
requires several environment variables to be set to find the shared
library files and error message catalog.
For more information, refer to this section in the giraffez
documentation:
http://www.capitalone.io/giraffez/intro.html#environment.
"""

class GiraffeTypeError(GiraffeError):
"""
Baseclass for all giraffez type errors.
Expand Down Expand Up @@ -95,6 +54,10 @@ class ConfigReadOnly(ConfigurationError):
Raised when a write is attempted on a configuration file was opened
in read mode.
"""
class InvalidCredentialsError(ConfigurationError):
"""
Raised when connection credentials are incorrect.
"""

class ConnectionLock(ConfigurationError):
"""
Expand All @@ -105,118 +68,3 @@ class ConnectionLock(ConfigurationError):
def __init__(self, dsn):
super(ConnectionLock, self).__init__(("Connection {0} is currently locked. please update "
"credentials and run:\n\tgiraffez config --unlock {0}").format(dsn))

class TeradataError(Exception):
"""
Baseclass for all Teradata errors. This exception represents any
error that originates from Teradata CLIv2/TPTAPI.
This will attempt to parse error codes from the message in the case
that the error is a Teradata CLIv2 error.
"""

def __init__(self, message, code=None):
super(TeradataError, self).__init__(message)

if isinstance(message, Exception):
#message = getattr(message, 'message', '')
message = str(message)


if code is None:
if isinstance(message, (list, tuple)):
code, message = message[0], message[1]
else:
if ":" in message:
parts = [x.strip() for x in message.split(":", 1)]
try:
code = int(parts[0])
message = parts[1]
except ValueError as error:
code = None
message = "Unable to parse CLI error code/message"
else:
code = None
message = "Unable to parse CLI error code/message"
if code == ObjectDoesNotExist.code:
raise suppress_context(ObjectDoesNotExist(message, code))
elif code == ObjectNotTable.code:
raise suppress_context(ObjectNotTable(message, code))
elif code == ObjectNotView.code:
raise suppress_context(ObjectNotView(message, code))
elif code == TransactionAborted.code:
raise suppress_context(TransactionAborted(message, code))
elif code == CannotReleaseMultiLoad.code:
raise suppress_context(CannotReleaseMultiLoad(message, code))
elif code == MultiLoadTableExists.code:
raise suppress_context(MultiLoadTableExists(message, code))
elif code == MultiLoadWorkTableNotFound.code:
raise suppress_context(MultiLoadWorkTableNotFound(message, code))
elif code == InvalidCredentialsError.code:
raise suppress_context(InvalidCredentialsError(message, code))

self.message = message
self.code = code

def __repr__(self):
return "{}: {}".format(self.code, self.message)

class ObjectDoesNotExist(TeradataError):
"""
Teradata object does not exist.
"""
code = 3807

class ObjectNotTable(TeradataError):
"""
Teradata object not a table.
"""
code = 3853

class ObjectNotView(TeradataError):
"""
Teradata object not a view.
"""
code = 3854

class TransactionAborted(TeradataError):
"""
Teradata transaction aborted.
"""
code = 2631

class MultiLoadError(TeradataError):
"""
General MultiLoad error class.
"""

class CannotReleaseMultiLoad(MultiLoadError):
"""
Raised when MultiLoad cannot be released because it is in the
apply phase.
"""
code = 2572

class MultiLoadTableExists(MultiLoadError):
"""
Raised when MultiLoad worktables exist.
"""
code = 2574

class MultiLoadLocked(MultiLoadError):
"""
Raised when MultiLoad appears to be locked. Used to determine when
MultiLoad should be released.
"""

class MultiLoadWorkTableNotFound(MultiLoadLocked):
"""
Raised when MultiLoad worktable is missing during restart.
"""
code = 2583

class InvalidCredentialsError(TeradataError):
"""
Raised when connection credentials are incorrect.
"""
code = 8017
36 changes: 17 additions & 19 deletions giraffez/export.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
from .constants import *
from .errors import *

from ._tpt import InvalidCredentialsError
from .config import Config
from .connection import Connection
from .encoders import dict_to_json, TeradataEncoder
Expand All @@ -45,15 +46,6 @@ class TeradataExport(Connection):
:param str host: Omit to read from :code:`~/.girafferc` configuration file.
:param str username: Omit to read from :code:`~/.girafferc` configuration file.
:param str password: Omit to read from :code:`~/.girafferc` configuration file.
:param str delimiter: The delimiter to use when exporting with the 'text'
encoding. Defaults to '|'
:param str null: The string to use to represent a null value with the
'text' encoding. Defaults to 'NULL'
:param str encoding: The encoding format in which to export the data.
Defaults to 'text'. Possible values are 'text' - delimited text
output, 'dict' - to output each row as a :code:`dict` object mapping column names
to values, 'json' - to output each row as a JSON encoded string, and
'archive' to output in giraffez archive format
:param int log_level: Specify the desired level of output from the job.
Possible values are :code:`giraffez.SILENCE` :code:`giraffez.INFO` (default),
:code:`giraffez.VERBOSE` and :code:`giraffez.DEBUG`
Expand All @@ -65,6 +57,7 @@ class TeradataExport(Connection):
locks the connection used in the configuration file. This can be unlocked using the
command :code:`giraffez config --unlock <connection>` changing the connection password,
or via the :meth:`~giraffez.config.Config.unlock_connection` method.
:param bool coerce_floats: Coerce Teradata decimal types into Python floats
:raises `giraffez.errors.InvalidCredentialsError`: if the supplied credentials are incorrect
:raises `giraffez.errors.TeradataError`: if the connection cannot be established
Expand All @@ -76,7 +69,7 @@ class TeradataExport(Connection):
with giraffez.Export('dbc.dbcinfo') as export:
print(export.header)
for row in export.results():
for row in export.values():
print(row)
"""

Expand Down Expand Up @@ -155,6 +148,8 @@ def header(self):
"""
if self.query is None:
raise GiraffeError("Must set target table or query.")
if not self.delimiter:
self.delimiter = DEFAULT_DELIMITER
return self.delimiter.join(self.columns.names)

def initiate(self):
Expand Down Expand Up @@ -205,7 +200,17 @@ def query(self, query):
else:
self._query = statement
self.initiated = False
self.export.set_query(statement)
# Since CLIv2 is used in set_query (instead of relying on the
# colums from the TPT Export driver) and set_query will always
# happen before calls to initiate, set_query will always fail
# with InvalidCredentialsError before initiate despite initiate
# presumably failing after this point as well.
try:
self.export.set_query(statement)
except InvalidCredentialsError as error:
if args.protect:
Config.lock_connection(args.conf, args.dsn, args.key)
raise error

def archive(self, writer):
if 'b' not in writer.mode:
Expand Down Expand Up @@ -266,14 +271,7 @@ def fetchall(self):
if self.query is None:
raise GiraffeError("Must set target table or query.")
if not self.initiated:
# TODO: this may not be necessary, InvalidCredentialsError
# is being checked in the base class.
try:
self.initiate()
except InvalidCredentialsError as error:
if self.protect:
Config.lock_connection(self.config, self.dsn)
raise suppress_context(error)
self.initiate()
while True:
data = self.export.get_buffer()
if not data:
Expand Down
Loading

0 comments on commit 93b4094

Please sign in to comment.