简体   繁体   English

Python子进程模块| 带密码的 Postgres pg_dump

[英]Python Subprocess Module | Postgres pg_dump with password

I have a database that I want to back up with my a python code.我有一个我想用我的 python 代码备份的数据库。

I tried to base my code from the code in this discussion that uses the subprocess module and pg_dump .我试图根据使用 subprocess 模块和pg_dump 讨论中的代码来编写我的代码。 My problem now is that I have to manually type in the password to get the backup file.我现在的问题是我必须手动输入密码才能获取备份文件。 I read somewhere that doing a .pgpass but I do want to see if it is possible to do it within the subprocess module.我在某处读到了做.pgpass但我确实想看看是否可以在.pgpass模块中执行此操作。

My code follows below:我的代码如下:

from subprocess import Popen, PIPE
from pathlib import Path, PureWindowsPath

def backup():
    version = 11
    postgresDir = Path("C:/Program Files/PostgreSQL/{}/bin/".format(version))
    directory = PureWindowsPath(postgresDir)
    filename = 'myBackUp2'  # output filename here
    saveDir = Path("D:/Desktop/{}.tar".format(filename))  # output directory here
    file = PureWindowsPath(saveDir)

    host = 'localhost'
    user = 'postgres'
    port = '5434'
    dbname = 'database_name'  # database name here
    proc = Popen(['pg_dump', '-h', host, '-U', user, '-W', '-p', port,
                   '-F', 't', '-f', str(file), '-d', dbname],
                    cwd=directory, shell=True, stdin=PIPE)
    proc.wait()

backup()

The code above works and the backup is created is I type in the password.上面的代码有效,备份是在我输入密码时创建的。 I tried to replace proc.wait() with the code below to remove the need of typing the password manually:我尝试用下面的代码替换proc.wait()以消除手动输入密码的需要:

return proc.communicate('{}\n'.format(database_password))

But I would receive this error:但我会收到这个错误:

TypeError: a bytes-like object is required, not 'str'类型错误:需要类似字节的对象,而不是“str”

Is this possible to do within subprocess?这可以在子流程中进行吗? If so, how?如果是这样,如何?

Use a password file.使用密码文件。

On Microsoft Windows the file is named %APPDATA%\\postgresql\\pgpass.conf (where %APPDATA% refers to the Application Data subdirectory in the user's profile).在 Microsoft Windows 上,该文件名为%APPDATA%\\postgresql\\pgpass.conf (其中%APPDATA%指的是用户配置文件中的 Application Data 子目录)。

and the -w or --no-password command line option (instead of -W )-w--no-password命令行选项(而不是-W

-w

--no-password

Never issue a password prompt.永远不要发出密码提示。 If the server requires password authentication and a password is not available by other means such as a .pgpass file, the connection attempt will fail.如果服务器需要密码验证并且密码不能通过其他方式(例如 .pgpass 文件)获得,则连接尝试将失败。 This option can be useful in batch jobs and scripts where no user is present to enter a password.此选项在没有用户输入密码的批处理作业和脚本中很有用。

最简单的方法是使用PGPASSWORD 环境变量

There is two classes:有两个类:

  1. First class needed for create dsn string.创建 dsn 字符串所需的第一类。 Then try to connect with dsn parameters.然后尝试用dsn参数连接。 If cannot connect go to Second class.如果无法连接到二等舱。
  2. Second class needed for create for create DataBase and restore all tables from file.创建创建数据库和从文件恢复所有表所需的第二个类。 You need to remake this strings:您需要重新制作此字符串:

for correctly open your DataBase dump_file正确打开您的数据库 dump_file

__folder_name = Path(__file__).parent.parent
__folder_name_data = os.path.join(__folder_name, 'data')
__file_to_open = os.path.join(__folder_name_data, 'bd.backup')

import os
import textwrap
from pathlib import Path
from subprocess import Popen, PIPE


class DataBaseAPI:
    __slots__ = ('__dsn', 'cur')

    def __init__(self):
        self.__dsn = self.__dsn_string()
        self.cur = self.__connection()

    @staticmethod
    def __dsn_string() -> dict:
        print(f'INPUT name of DataBase')
        name = input()
        print(f'INPUT password of DataBase')
        password = input()
        print(f'INPUT user_name of DataBase or press ENTER if user_name="postgres"')
        user_name = input()
        if len(user_name) == 0:
            user_name = 'postgres'
        print(f'INPUT host_name of DataBase or press ENTER if host_name="localhost"')
        host_name = input()
        if len(host_name) == 0:
            host_name = 'localhost'
        return {'dbname': name, 'user': user_name, 'password': password, 'host': host_name}

    def __connection(self):
        try:
            conn = psycopg2.connect(dbname=self.__dsn['dbname'], user=self.__dsn['user'],
                                    host=self.__dsn['host'], password=self.__dsn['password'], port=5432)
        except psycopg2.OperationalError:
            print(textwrap.fill(f'There is no existing DataBase. Creating new DataBase', 80,
                                   subsequent_indent='                   '))
            DataBaseCreator(self.__dsn)
            conn = psycopg2.connect(dbname=self.__dsn['dbname'], user=self.__dsn['user'],
                                    host=self.__dsn['host'], password=self.__dsn['password'], port=5432)
        finally:
            conn.autocommit = True
            cur = conn.cursor()
            print(f'DataBase connection complete')
            return cur



class DataBaseCreator:
    def __init__(self, dsn):
        self.__dsn = dsn
        self.__check_conf_file()
        self.__create_data_base()
        self.__restore_data_base()

    def __check_conf_file(self):
        __app_data = os.environ.copy()["APPDATA"]
        __postgres_path = Path(f'{__app_data}\postgresql')
        __pgpass_file = Path(f'{__postgres_path}\pgpass.conf')
        parameters = f'{self.__dsn["host"]}:{5432}:{self.__dsn["dbname"]}:' \
                     f'{self.__dsn["user"]}:{int(self.__dsn["password"])}\n'
        if not os.path.isdir(__postgres_path):
            os.makedirs(__postgres_path)
        if os.path.isfile(__pgpass_file):
            log.debug(f'File "pgpass.conf" already exists')
            with open(__pgpass_file, 'r+') as f:
                content = f.readlines()
                if parameters not in content:
                    # сервер: порт:база_данных: имя_пользователя:пароль
                    f.write(parameters)
                else:
                    log.info(f' {parameters} already in "pgpass.conf" file')
        else:
            log.debug(f'File "pgpass.conf" not exists')
            with open(__pgpass_file, 'x') as f:
                # сервер: порт:база_данных: имя_пользователя:пароль
                f.write(parameters)

    def __create_data_base(self):
        try:
            __conn = psycopg2.connect(dbname='postgres', user=self.__dsn['user'],
                                      host=self.__dsn['host'], password=self.__dsn['password'], port=5432)
        except Exception as _:
            log.exception(f'{_}')
        else:
            __conn.autocommit = True
            __cur = __conn.cursor()
            __query = f'CREATE DATABASE "{self.__dsn["dbname"]}"'
            __cur.execute(__query)
            log.info(f'{__query}')

    def __restore_data_base(self):
        __col = [x for x in self.__dsn.values()]
        __folder_name = Path(__file__).parent.parent
        __folder_name_data = os.path.join(__folder_name, 'data')
        __file_to_open = os.path.join(__folder_name_data, 'bd.backup')
        __cmd = f'pg_restore --host={__col[3]} --dbname={__col[0]} --username={__col[1]} ' \
                f'--verbose=True --no-password ' \
                f'{__file_to_open}'
        try:
            __proc = Popen(__cmd, stdout=PIPE, stderr=PIPE)
        except FileNotFoundError:
            log.info(f'FileNotFoundError: [WinError 2] Не удается найти указанный файл')
            log.info(textwrap.fill(f'You need to SET Windows $PATH for use "pg_restore" in cmd', 80,
                                   subsequent_indent='                   '))
        else:
            __stderr = __proc.communicate()[1].decode('utf-8', errors="ignore").strip()
            log.debug(textwrap.fill(f'{__stderr}', 80))

dbname一种选择是使用dbname参数

'pg_dump --dbname=postgresql://{}:{}@{}:{}/{}'.format(user, password, host, port, database_name)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM