Python cannot connect to MySQL if declare in a class - python

I'm newbie in Python. I'm trying to use Python to connect MySQL Server. I wrote like guides from MySQL official page, it was OK. But, when I create a connector class, it raised the error "MySQL Connection not available"
Here is my class
import mysql.connector
from mysql.connector import errorcode
## BEGIN MySQL Connector Class
class MySQLConnector :
configs = {
"user":"root",
"password":"",
"host":"127.0.0.1",
"database":"python_db",
"raise_on_warnings": True
}
cursor = None
connection = None
## BEGIN Constructor
def __init__(self, configs = {}) :
if(any(configs)!=False) :
self.configs = configs
## END Constructor
## BEGIN Open
def open(self) :
try:
self.connection = mysql.connector.connect(self.configs)
except mysql.connector.Error as err:
if err.errno == errorcode.ER_ACCESS_DENIED_ERROR:
print("Something is wrong with your user name or password")
elif err.errno == errorcode.ER_BAD_DB_ERROR:
print("Database does not exists")
else:
print(err)
finally:
self.connection.close()
return self.connection
## END Open
## BEGIN close connection
def close(self) :
self.cursor.close()
self.connection.close()
## END close connection
## BEGIN execute
def execute(self, query) :
if(self.connection == None) :
print("Connection is None")
return
self.cursor = self.connection.cursor()
if(self.cursor!=None) :
self.cursor.execute(query)
else:
print("Cursor is 'None'")
## END execute
## END MySQL Connector Class
## BEGIN RUN
objConnect = MySQLConnector()
objConnect.open()
objConnect.execute("SELECT * FROM User")
Please show me the way to solution and explained me why my code has error.
Thanks!
EDITED
Finally, mata and alecxe help me to solve this problem, I don't know which solution to be choosen. I summary here for someone has mistake like me:
1. Remove the finally statement.
2. Using ** in self.connection = mysql.connector.connect(**self.configs)

Even if you correct the error alecxe pointed out, your code still won't work.
The finally block ensures that each connection is closed before it is returned, no matter wheather there was an exception or not, so the open method only returns closed connections.

You are passing a dictionary object self.configs into mysql.connector.connect, though, according to docs, you should pass to it user, password and other arguments. Looks like you need to unpack configs:
self.connection = mysql.connector.connect(**self.configs)
Hope this is it.

Related

Python MySQL - best practises. Create & connect

could you advise me on best practices on how to deal with MySQL class in Python application?
class DBClient:
def __init__(self, db_name=None):
self.conn = None
self.db_name = db_name
self.__create_connection()
def __create_connection(self):
try:
self.conn = mysql.connector.connect(
host="127.0.0.1",
port=3306,
user="root",
password="password",
database=self.db_name
)
print('DB Connection successful')
except InterfaceError as e:
exit(e)
except mysql.connector.Error as err:
if err.errno == errorcode.ER_ACCESS_DENIED_ERROR:
print("Something is wrong with your user name or password")
elif err.errno == errorcode.ER_BAD_DB_ERROR:
print("Database does not exist")
else:
print(err)
def __del__(self):
if self.conn:
self.conn.close()
def __commit(self):
self.conn.commit()
DBClient(config.DB_NAME).create_items_table()
in general the problem is:
as you noticed the method __create_connestion specified to which database we would like to connect. The problem is if the the database is not created. How to avoid code duplication in that scenario:
connect to DB
if db is not created -> create that
if db is created -> connect
It is not a good idea to create a database automatically, it is an action that is better to do yourself.
Never put credentials in code, use environment variables instead, please have a look at this module
Dotenv
so if I understand correctly it is better to have sth like this in main:
if __name__ == '__main__':
DBClient(config.DB_NAME).to_do_something()
and have separate script, lets say setup_db.py which is going to create a database and schema?

Python - Connecting to Snowflake - Name Error

I'm writing some Python code to connect to our Snowflake Database and getting an error in my Python (I'm new to Python so i've likely done something wrong here).
I'm getting <class 'NameError'> TestConnection.py 98 inside my script, which has the relevant parts below
def connection(obj):
try:
global cur, engine
cur = obj.cursor_connection().cursor()
engine = obj.engine_connection().engine.connect()
except Exception as e:
print(e)
sql = "select current_warehouse(), current_database(), current_schema();"
try:
print("Cursor connection successful")
except Exception as e:
print(e)
try:
print("Engine connection successful")
except Exception as e:
print(e)
return cur, engine
try:
#Setup Connection with both cursor and the engine
db, schema= login.db, login.schema
obj = connect(db, schema)
cur , engine = connection(obj)
The line I'm getting the error on is the cur,engine = connection(obj) part.
I had a previous error before (UnboundLocalError) but putting global cur, engine inside the connection function fixed that, but getting NameError now.
What am I doing wrong here?
Thanks
try this
import snowflake.connector
ctx = snowflake.connector.connect(
user='<user_name>',
password='<password>',
account='<account_identifier>'
)
cs = ctx.cursor()
Ok, I've fixed this now, it was indentation on the functions and class.
I ended up with
class connect:
def __init__
def cursor_connection
def engine_connection
def connection
and that has it working whereas having def_connection tabbed in was breaking it.

python try/except in loop

Im using python 3 and i have this loop, which iterate on this list -
self.settings["db"]["host"] = ["db-0", "db-1"]
My problem is that it seems to send in return self.conn the first option all the time,
db-0 instead of trying with db-1
I have 2 db container servers, and when i stop one of them - for example db-0 it should try psycopg2.connect with db-1
def db_conn(self):
try:
for server in self.settings["db"]["host"]:
self.conn = psycopg2.connect(
host=server,
user=self.settings["db"]["user"],
password=self.settings["db"]["password"],
database=self.settings["db"]["database"],
connect_timeout=5,
)
return self.conn
except Exception:
pass
if loop has not succeeded i dont want it to return self.conn, only if the try worked.
I also tried :
def db_conn(self):
try:
for server in self.settings["db"]["host"]:
self.conn = psycopg2.connect(
host=server,
user=self.settings["db"]["user"],
password=self.settings["db"]["password"],
database=self.settings["db"]["database"],
connect_timeout=5,
)
except Exception:
pass
return self.conn
You are looping within a try.
Do it the other way around,
push the try down within the loop.
The DbC contract the current code is attempting to offer
is to return a valid connection.
Let's make that more explicit, by writing a very simple helper.
We will spell out its contract in a docstring.
def first_true(iterable, default=False, pred=None):
# from https://docs.python.org/3/library/itertools.html
return next(filter(pred, iterable), default)
def get_conn_or_none(self, server):
"""Returns DB connection to server, or None if that's not possible."""
try:
return psycopg2.connect(
host=server,
user=self.settings["db"]["user"],
password=self.settings["db"]["password"],
database=self.settings["db"]["database"],
connect_timeout=5,
)
except Exception:
return None
Now db_conn is simply:
def db_conn(self):
return first_true(map(self.get_conn_or_none, self.settings["db"]["host"]))
That uses the same logic as in your question.
You may want to have db_conn additionally check whether
all connection attempts failed, and raise fatal error in that case.
BTW, it's very odd that you're apparently storing a list of server hostnames
in self.settings["db"]["host"], given that an individual user / pw / db is
stored in the other fields.
Consider renaming that list to self.servers.
You can try to check the connection status before returning it:
def db_conn(self):
try:
for server in self.settings["db"]["host"]:
self.conn = psycopg2.connect(
host=server,
user=self.settings["db"]["user"],
password=self.settings["db"]["password"],
database=self.settings["db"]["database"],
connect_timeout=5,
)
**if self.conn.isOk()**
return self.conn
except Exception:
pass
This code worked for me:
def db_conn(self):
for server in self.settings["db"]["host"]:
try:
self.print_info("TRYING", server)
self.conn = psycopg2.connect(
host=server,
user=self.settings["db"]["user"],
password=self.settings["db"]["password"],
database=self.settings["db"]["database"],
)
except:
self.print_info("SERVER DOWN", server)
continue
return self.conn
continue will continue the rest of the code if i get exception ( failed connection)
then it goes back to for loop with the second item in list.

MySQL Connector/Python not closing connection explicitly

I have the following:
class FooData(object):
def __init__(self):
...
try:
self.my_cnf = os.environ['HOME'] + '/.my.cnf'
self.my_cxn = mysql.connector.connect(option_files=self.my_cnf)
self.cursor = self.my_cxn.cursor(dictionary=True)
except mysql.connector.Error as err:
if err.errno == 2003:
self.my_cnf = None
self.my_cxn = None
self.cursor = None
I am able to use my_cxn and cursor without any obvious failure. I never explicitly terminate the connection, and have observed the following messages in my mysql error log though...
2017-01-08T15:16:09.355190Z 132 [Note] Aborted connection 132 to db:
'mydatabase' user: 'myusername' host: 'localhost'
(Got an error reading communication packets)
Am I going about this the wrong way? Would it be more efficient for me to initialize my connector and cursor every time I need to run a query?
What do I need to look for on the mysql config to avoid these aborted connection?
Separately, I also observe these messages in my error logs frequently:
2017-01-06T15:28:45.203067Z 0 [Warning] Changed limits: max_open_files: 1024
(requested 5000)
2017-01-06T15:28:45.205191Z 0 [Warning] Changed limits: table_open_cache: 431
(requested 2000)
Is it related to the above? What does it mean and how can I resolve it?
I tried various solutions involving /lib/systemd/system/mysql.service.d/limits.conf and other configuration settings but couldn't get any of them to work.
It's not a config issue. When you are done with a connection you should close it by explicitly calling close. It is generally a best practice to maintain the connection for a long time as creating one takes time. It's not possible to tell from your code snippet where would be the best place to close it - it's whenever you're "done" with it; perhaps at the end of your __main__ method. Similarly, you should close the cursor explicitly when your done with it. Typically that happens after each query.
So, maybe something like:
class FooData(object):
def __init__(self):
...
try:
self.my_cnf = os.environ['HOME'] + '/.my.cnf'
self.my_cxn = mysql.connector.connect(option_files=self.my_cnf)
def execute_some_query(self, query_info):
"""Runs a single query. Thus it creates a cursor to run the
query and closes it when it's done."""
# Note that cursor is not a member variable as it's only for the
# life of this one query
cursor = self.my_cxn.cursor(dictionary=True)
cursor.execute(...)
# All done, close the cursor
cursor.close()
def close():
"""Users of this class should **always** call close when they are
done with this class so it can clean up the DB connection."""
self.my_cxn.close()
You might also look into the Python with statement for a nice way to ensure everything is always cleaned up.
I rewrote my class above to look like this...
class FooData(object):
def __init__(self):
self.myconfig = {
'option_files': os.environ['HOME'] + '/.my.cnf',
'database': 'nsdata'
}
self.mysqlcxn = None
def __enter__(self):
try:
self.mysqlcxn = mysql.connector.connect(**self.myconfig)
except mysql.connector.Error as err:
if err.errno == 2003:
self.mysqlcxn = None
return self
def __exit__(self, exc_type, exc_value, traceback):
if self.mysqlcxn is not None and self.mysqlcxn.is_connected():
self.mysqlcxn.close()
def etl(self)
...
I can then use with ... as and ensure that I am cleaning up properly.
with FooData() as obj:
obj.etl()
The Aborted connection messages are thus properly eliminated.
Oliver Dain's response set me on the right path and Explaining Python's '__enter__' and '__exit__' was very helpful in understanding the right way to implement my Class.

Calling Class, getting TypeError: unbound method must be called [duplicate]

This question already has answers here:
unbound method f() must be called with fibo_ instance as first argument (got classobj instance instead)
(8 answers)
Closed 6 years ago.
I have reviewed the error on Stackoverflow, but none of the solutions I've seen resolve my problem. I'm attempting to create a class for cx_Oracle to put my database connectivity in a class, and call it during my database instances.
I've created similar classes in C#, but python is especially difficult for some reason. Any assistance appreciated.
I leveraged this code found here:
cx_Oracle and Exception Handling - Good practices?
import sys
import os
import cx_Oracle
class Oracle(object):
__db_server = os.getenv("ORACLE_SERVER")
__db_user = os.getenv("ORACLE_ACCT")
__db_password = os.getenv("ORACLE_PWD")
def connect(self):
""" Connect to the database. """
try:
self.db = cx_Oracle.connect(__db_user+'/'+__db_password+'#'+__db_server)
except cx_Oracle.DatabaseError as e:
error, = e.args
if error.code == 1017:
print('Please check your credentials.')
else:
print('Database connection error: %s'.format(e))
# Very important part!
raise
# If the database connection succeeded create the cursor
# we-re going to use.
self.cursor = db.Cursor()
def disconnect(self):
"""
Disconnect from the database. If this fails, for instance
if the connection instance doesn't exist we don't really care.
"""
try:
self.cursor.close()
self.db.close()
except cx_Oracle.DatabaseError:
pass
def execute(self, sql, bindvars=None, commit=False):
"""
Execute whatever SQL statements are passed to the method;
commit if specified. Do not specify fetchall() in here as
the SQL statement may not be a select.
bindvars is a dictionary of variables you pass to execute.
"""
try:
self.cursor.execute(sql, bindvars)
except cx_Oracle.DatabaseError as e:
error, = e.args
if error.code == 955:
print('Table already exists')
elif error.code == 1031:
print("Insufficient privileges")
print(error.code)
print(error.message)
print(error.context)
# Raise the exception.
raise
# Only commit if it-s necessary.
if commit:
self.db.commit()
def select(self, sql, commit=False):
bindvars=None
result = None
try:
self.cursor.execute(sql, bindvars)
result = self.cursor.fetchall()
except cx_Oracle.DatabaseError as e:
error, = e.args
print "Database Error: failed with error code:%d - %s" % (error.code, error.message)
raise
if commit:
self.db.commit()
return result
def commit(self):
try:
self.db.commit()
except cx_Oracle.DatabaseError as e:
error, = e.args
print "Database Commit failed with error code:%d - %s" % (error.code, error.message)
raise
def rollback(self):
try:
self.db.rollback()
except cx_Oracle.DatabaseError as e:
error, = e.args
print "Database Rollback failed with error code:%d - %s" %(error.code, error.message)
raise
And this is my calling routine
import sys
import os
#import cx_Oracle
from Oracle import Oracle
def main():
oracle = Oracle.connect()
query = """SELECT DISTINCT NAME FROM MyTable"""
data = oracle.select(query)
for row in data:
print row
oracle.disconnect()
### MAIN
if __name__ == '__main__':
main()
On a related note: I can't seem to get Python to find my Oracle.py class, unless it's in the same directory as the calling function.
Allan
You have to create an instance of your class to use it:
orcl = Oracle()
orcl.connect()
...

Categories

Resources