2009-10-21 22 views
7

Tôi muốn mã của mình tự động thử nhiều cách để tạo kết nối cơ sở dữ liệu. Ngay sau khi một công trình, mã cần phải di chuyển (tức là nó không nên thử các cách khác nữa). Nếu tất cả đều thất bại, thì kịch bản chỉ có thể nổ tung.Thành ngữ Python cho 'Thử cho đến khi không có ngoại lệ nào được nêu ra'

Vì vậy, trong - những gì tôi nghĩ là, nhưng rất có thể là không - một cơn đột quỵ của thiên tài tôi đã cố gắng này:

import psycopg2 
from getpass import getpass 

# ouch, global variable, ooh well, it's just a simple script eh 
CURSOR = None 

def get_cursor(): 
    """Create database connection and return standard cursor.""" 

    global CURSOR 

    if not CURSOR: 
     # try to connect and get a cursor 
     try: 
      # first try the bog standard way: db postgres, user postgres and local socket 
      conn = psycopg2.connect(database='postgres', user='postgres') 
     except psycopg2.OperationalError: 
      # maybe user pgsql? 
      conn = psycopg2.connect(database='postgres', user='pgsql') 
     except psycopg2.OperationalError: 
      # maybe it was postgres, but on localhost? prolly need password then 
      conn = psycopg2.connect(database='postgres', user='postgres', host='localhost', password=getpass()) 
     except psycopg2.OperationalError: 
      # or maybe it was pgsql and on localhost 
      conn = psycopg2.connect(database='postgres', user='pgsql', host='localhost', password=getpass()) 

     # allright, nothing blew up, so we have a connection 
     # now make a cursor 
     CURSOR = conn.cursor() 

    # return existing or new cursor 
    return CURSOR 

Nhưng có vẻ như thứ hai và tiếp theo trừ báo cáo không bắt OperationalErrors nữa không. Có lẽ bởi vì Python chỉ bắt một ngoại lệ một lần trong một thử ... ngoại trừ tuyên bố?

Vậy sao? Nếu không: có điều gì khác tôi đang làm sai? Nếu vậy: làm thế nào để bạn làm một cái gì đó như thế này sau đó? Có thành ngữ tiêu chuẩn nào không?

(Tôi biết có những cách giải quyết vấn đề này, giống như có người sử dụng xác định các thông số kết nối trên dòng lệnh, nhưng đó không phải là câu hỏi của tôi ok :))

EDIT:

tôi chấp nhận câu trả lời xuất sắc của retracile và tôi đã nhận xét của gnibbler về việc sử dụng cấu trúc for..else. Mã cuối cùng đã trở thành (xin lỗi, tôi không thực sự theo các ký tự tối đa cho mỗi giới hạn dòng từ pep8):

EDIT 2: Như bạn có thể thấy từ nhận xét về lớp Con trỏ: Tôi thực sự không biết cách gọi loại này. Nó không thực sự là một singleton (tôi có thể có nhiều trường hợp khác nhau của con trỏ) nhưng khi gọi get_cursor tôi nhận được cùng một đối tượng con trỏ mọi lúc. Vì vậy, nó giống như một nhà máy singleton? :)

import psycopg2 
from getpass import getpass 
import sys 

class UnableToConnectError(Exception): 
    pass 

class Cursor: 
    """Cursor singleton factory?""" 

    def __init__(self): 
     self.CURSOR = None 

    def __call__(self): 
     if self.CURSOR is None: 
      # try to connect and get a cursor 
      attempts = [ 
        {'database': 'postgres', 'user': 'postgres'}, 
        {'database': 'postgres', 'user': 'pgsql'}, 
        {'database': 'postgres', 'user': 'postgres', 'host': 'localhost', 'password': None}, 
        {'database': 'postgres', 'user': 'pgsql', 'host': 'localhost', 'password': None}, 
        ] 

      for attempt in attempts: 
       if 'password' in attempt: 
        attempt['password'] = getpass(stream=sys.stderr) # tty and stderr are default in 2.6, but 2.5 uses sys.stdout, which I don't want 
       try: 
        conn = psycopg2.connect(**attempt) 

        attempt.pop('password', None) 
        sys.stderr.write("Succesfully connected using: %s\n\n" % attempt) 

        break # no exception raised, we have a connection, break out of for loop 
       except psycopg2.OperationalError: 
        pass 
      else: 
       raise UnableToConnectError("Unable to connect: exhausted standard permutations of connection dsn.") 

      # allright, nothing blew up, so we have a connection 
      # now make a cursor 
      self.CURSOR = conn.cursor() 

     # return existing or new cursor 
     return self.CURSOR 
get_cursor = Cursor() 

Trả lời

14

Khoảng:

attempts = [ 
    { 'database'='postgres', 'user'='pgsql', ...}, 
    { 'database'='postgres', 'user'='postgres', 'host'='localhost', 'password'=getpass()}, 
    ... 
] 
conn = None 
for attempt in attempts: 
    try: 
     conn = psycopg2.connect(**attempt) 
     break 
    except psycopg2.OperationalError: 
     pass 
if conn is None: 
    raise a ruckus 
CURSOR = conn.cursor() 

Bây giờ, nếu bạn không muốn gọi getpass() trừ khi nó là cần thiết, bạn muốn kiểm tra if 'password' in attempt: attempt['password'] = getpass() hoặc lâu hơn.

Bây giờ về điều đó toàn cầu ....

class MyCursor: 
    def __init__(self): 
     self.CURSOR = None 
    def __call__(self): 
     if self.CURSOR is None: 
      <insert logic here> 
     return self.CURSOR 

get_cursor = MyCursor() 

... mặc dù tôi nghĩ rằng có một vài cách khác để thực hiện được điều tương tự.

Đưa nó tất cả cùng nhau:

class MyCursor: 
    def __init__(self): 
     self.CURSOR = None 
    def __call__(self): 
     if self.CURSOR is None: 
      attempts = [ 
       {'database'='postgres', 'user'='postgres'}, 
       {'database'='postgres', 'user'='pgsql'}, 
       {'database'='postgres', 'user'='postgres', 'host'='localhost', 'password'=True}, 
       {'database'='postgres', 'user'='pgsql', 'host'='localhost', 'password'=True}, 
      ] 
      conn = None 
      for attempt in attempts: 
       if 'password' in attempt: 
        attempt['password'] = getpass() 
       try: 
        conn = psycopg2.connect(**attempt) 
        break # that didn't throw an exception, we're done 
       except psycopg2.OperationalError: 
        pass 
      if conn is None: 
       raise a ruckus # nothin' worked 
      self.CURSOR = conn.cursor() 
     return self.CURSOR 
get_cursor = MyCursor() 

Lưu ý: hoàn toàn chưa được kiểm tra

+3

Thay vì khởi tạo 'conn' thành' None', bạn chỉ có thể sử dụng mệnh đề 'else' trên' for' –

+0

+1 @David: ngắt đảm bảo rằng kết nối đầu tiên hoạt động được trả về. Giải pháp này sẽ mở rộng tốt với số lượng cơ sở dữ liệu tăng lên, và các chuỗi kết nối đã được tóm tắt độc đáo từ mã. –

+1

@gnibbler - Woah, tôi quên Python đã có tính năng tuyệt vời đó (http://docs.python.org/reference/compound_stmts.html # the-for-statement) –

-1

Bạn đang gần. Có lẽ điều tốt nhất để làm trong trường hợp này là làm tổ các nỗ lực thứ hai và tiếp theo trong khối ngoại trừ. Do đó, phần quan trọng của mã của bạn sẽ trông giống như:

if not CURSOR: 
    # try to connect and get a cursor 
    try: 
     # first try the bog standard way: db postgres, user postgres and local socket 
     conn = psycopg2.connect(database='postgres', user='postgres') 
    except psycopg2.OperationalError: 
     # maybe user pgsql? 
     try: 
      conn = psycopg2.connect(database='postgres', user='pgsql') 
     except psycopg2.OperationalError: 
      # maybe it was postgres, but on localhost? prolly need password then 
      try: 
       conn = psycopg2.connect(database='postgres', user='postgres', host='localhost', password=getpass()) 
      except psycopg2.OperationalError: 
       # or maybe it was pgsql and on localhost 
       conn = psycopg2.connect(database='postgres', user='pgsql', host='localhost', password=getpass()) 
+2

-1 Ugh, hoàn toàn không phải là pythonic. Làm cho bạn trông giống như một lập trình viên C++ COM. –

+0

Khá xấu xí, vâng. – grasshopper

Các vấn đề liên quan