2009-10-21 2 views
7

코드가 자동으로 여러 가지 방법으로 데이터베이스 연결을 만들려고합니다. 하나가 작동하자마자 코드를 계속 옮겨야합니다 (즉, 다른 방법으로 시도해서는 안 됨). 그들 모두가 잘 실패하면 스크립트는 그냥 날아갈 수 있습니다.'예외가 발생하지 않을 때까지 시도하십시오'에 대한 Python 관용구

그래서에서 - 나는라고 생각하지만, 가장 가능성이없는 것 - 천재의 스트로크가 나는이 시도 :

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 

을하지만 문을 제외하고 두 번째 이후는 OperationalErrors를 잡는하지 않는 것 같다 더 이상. 아마 파이썬은 try ... except 문에서 한 번만 예외를 catch하기 때문에?

그렇습니까? 그렇지 않다면 내가 잘못하고있는 것이 있습니까? 그렇다면 다음과 같이 어떻게 할 것인가? 표준 관용구가 있습니까?

(I은 사용자가 명령 줄에서 연결 매개 변수를 지정할 수있는 같이이 문제를 해결 방법이 있습니다 알고 있지만 그 확인을 내 질문 : 아니다)

편집 :

I 허용 retracile의 훌륭한 대답과 for..else 구문을 사용하여 gnibbler의 의견을 들었다. 최종 코드는 다음과 같이되었습니다. (필자는 pep8에서 라인 제한 당 최대 문자 수를 실제로 따라갈 수 없습니다) :

EDIT 2 : 커 서 클래스에 대한 의견에서 알 수 있듯이 : 이런 종류의 클래스를 호출하는 방법. 그것은 실제로는 싱글 톤이 아닙니다 (저는 여러개의 Cursor 인스턴스를 가질 수 있습니다) 그러나 get_cursor를 호출 할 때마다 매번 동일한 커서 객체를 얻습니다. 그래서 싱글 톤 공장 같습니까? : 약

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() 

답변

14

은 : 당신이 필요한 경우가 아니면 getpass()를 호출하지 않으려면

이제
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() 

, 당신은 if 'password' in attempt: attempt['password'] = getpass() 정도를 검사 할 것입니다.

이제

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() 

.... 그 세계에 대한 ... 나는 같은 일을 수행하는 다른 몇 가지 방법이 있다고 생각하지만.

모두 함께 가져 오는 :

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() 

참고 :이 가까이있어

+3

대신 None''에'conn'를 초기화하는, 당신은 단지'for' –

+0

+1 @ 데이비드의'else' 절을 사용할 수 있습니다 : 휴식 작업 한 첫 번째 연결이 반환되는지 확인합니다. 이 솔루션은 데이터베이스 수의 증가에 따라 확장 성이 좋으며 연결 문자열은 코드에서 적절히 추출됩니다. –

+1

@gnibbler - Woah, 파이썬에는 멋진 기능이 있다는 것을 잊었습니다 (http://docs.python.org/reference/compound_stmts.html # the-for-statement) –

-1

완전히 테스트되지 않은가. 이 경우 가장 좋은 방법은 except 블록에서 두 번째 및 후속 시도를 중첩하는 것입니다. 따라서 코드의 중요한 부분의 모습 :

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 우물, 완전히 pythonic하지 않습니다. C++ COM 프로그래머처럼 보입니다. –

+0

꽤 못생긴 예. – grasshopper

관련 문제