首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Python脚本退出时没有输出,我也不知道为什么

Python脚本退出时没有输出,我也不知道为什么
EN

Stack Overflow用户
提问于 2012-09-06 20:39:56
回答 1查看 2.6K关注 0票数 1

我正在尝试调试一个Subversion后提交钩子,它调用了一些python脚本。到目前为止,我能够确定的是,当我手动运行post-commit.bat (我为它创建了一个包装器以使它更容易)时,一切都成功了,但是当SVN运行时,一个特定的步骤不起作用。

我们使用的是CollabNet SVNServe,我知道从文件中会删除所有的环境变量。这已经引起了一些问题,但现在不应该是一个问题。

在Subversion调用钩子脚本之前,它会从环境中删除所有变量-包括Unix上的$PATH和Windows上的%PATH%。因此,只有在拼写出程序的绝对名称时,脚本才能运行另一个程序。

post-commit.bat的相关部分是:

代码语言:javascript
复制
echo -------------------------- >> c:\svn-repos\company\hooks\svn2ftp.out.log

set SITENAME=staging
set SVNPATH=branches/staging/wwwroot/
"C:\Python3\python.exe" C:\svn-repos\company\hooks\svn2ftp.py ^
 --svnUser="svnusername" ^
 --svnPass="svnpassword" ^
 --ftp-user=ftpuser ^
 --ftp-password=ftppassword ^
 --ftp-remote-dir=/ ^
 --access-url=svn://10.0.100.6/company ^
 --status-file="C:\svn-repos\company\hooks\svn2ftp-%SITENAME%.dat" ^
 --project-directory=%SVNPATH% "staging.company.com" %1 %2 >> c:\svn-repos\company\hooks\svn2ftp.out.log

echo -------------------------- >> c:\svn-repos\company\hooks\svn2ftp.out.log

当我手动运行post-commit.bat时,例如:post-commit c:\svn-repos\company 12345,我在svn2ftp.out.log中看到了如下输出

代码语言:javascript
复制
-------------------------- 
args1: c:\svn-repos\company
args0: staging.company.com
abspath: c:\svn-repos\company
project_dir: branches/staging/wwwroot/
local_repos_path: c:\svn-repos\company
getting youngest revision...
done, up-to-date
-------------------------- 

但是,当我向回购提交一些东西并且它自动运行时,输出是:

代码语言:javascript
复制
-------------------------- 
-------------------------- 

svn2ftp.py有点长,所以我很抱歉,但现在开始了。我会有一些关于它下面的内容的注释/免责声明。

代码语言:javascript
复制
#!/usr/bin/env python

"""Usage: svn2ftp.py [OPTION...] FTP-HOST REPOS-PATH

Upload to FTP-HOST changes committed to the Subversion repository at 
REPOS-PATH.  Uses svn diff --summarize to only propagate the changed files

Options:

 -?, --help             Show this help message.

 -u, --ftp-user=USER    The username for the FTP server. Default: 'anonymous'

 -p, --ftp-password=P   The password for the FTP server. Default: '@'

 -P, --ftp-port=X       Port number for the FTP server. Default: 21

 -r, --ftp-remote-dir=DIR   The remote directory that is expected to resemble the
                        repository project directory

 -a, --access-url=URL   This is the URL that should be used when trying to SVN
                        export files so that they can be uploaded to the FTP 
                        server

 -s, --status-file=PATH   Required.  This script needs to store the last 
                          successful revision that was transferred to the 
                          server.  PATH is the location of this file.

 -d, --project-directory=DIR   If the project you are interested in sending to
                               the FTP server is not under the root of the 
                               repository (/), set this parameter.
                               Example: -d 'project1/trunk/'
                               This should NOT start with a '/'.
2008.5.2 CKS
Fixed possible Windows-related bug with tempfile, where the script didn't have
permission to write to the tempfile. Replaced this with a open()-created file
created in the CWD.

2008.5.13 CKS
Added error logging. Added exception for file-not-found errors when deleting files.

2008.5.14 CKS
Change file open to 'rb' mode, to prevent Python's universal newline support from
stripping CR characters, causing later comparisons between FTP and SVN to report changes.
"""

try:
    import sys, os
    import logging
    logging.basicConfig(
        level=logging.DEBUG,
        format='%(asctime)s %(levelname)s %(message)s',
        filename='svn2ftp.debug.log',
        filemode='a'
    )
    console = logging.StreamHandler()
    console.setLevel(logging.ERROR)
    logging.getLogger('').addHandler(console)

    import getopt, tempfile, smtplib, traceback, subprocess
    from io import StringIO
    import pysvn
    import ftplib
    import inspect

except Exception as e:
    logging.error(e)
    #capture the location of the error
    frame = inspect.currentframe()
    stack_trace = traceback.format_stack(frame)
    logging.debug(stack_trace)
    print(stack_trace)
    #end capture
    sys.exit(1)

#defaults
host = ""
user = "anonymous"
password = "@"
port = 21
repo_path = ""
local_repos_path = ""
status_file = ""
project_directory = ""
remote_base_directory = ""
toAddrs = "developers@company.com"
youngest_revision = ""

def email(toAddrs, message, subject, fromAddr='autonote@company.com'):
    headers = "From: %s\r\nTo: %s\r\nSubject: %s\r\n\r\n" % (fromAddr, toAddrs, subject)
    message = headers + message
    logging.info('sending email to %s...' % toAddrs)
    server = smtplib.SMTP('smtp.company.com')
    server.set_debuglevel(1)
    server.sendmail(fromAddr, toAddrs, message)
    server.quit()
    logging.info('email sent')

def captureErrorMessage(e):
    sout = StringIO()
    traceback.print_exc(file=sout)
    errorMessage = '\n'+('*'*80)+('\n%s'%e)+('\n%s\n'%sout.getvalue())+('*'*80)
    return errorMessage

def usage_and_exit(errmsg):
    """Print a usage message, plus an ERRMSG (if provided), then exit.
    If ERRMSG is provided, the usage message is printed to stderr and
    the script exits with a non-zero error code.  Otherwise, the usage
    message goes to stdout, and the script exits with a zero
    errorcode."""
    if errmsg is None:
        stream = sys.stdout
    else:
        stream = sys.stderr
    print(__doc__, file=stream)
    if errmsg:
        print("\nError: %s" % (errmsg), file=stream)
        sys.exit(2)
    sys.exit(0)


def read_args():
    global host
    global user
    global password
    global port
    global repo_path
    global local_repos_path
    global status_file
    global project_directory
    global remote_base_directory
    global youngest_revision

    try:
        opts, args = getopt.gnu_getopt(sys.argv[1:], "?u:p:P:r:a:s:d:SU:SP:",
            ["help",
            "ftp-user=",
            "ftp-password=",
            "ftp-port=",
            "ftp-remote-dir=",
            "access-url=",
            "status-file=",
            "project-directory=",
            "svnUser=",
            "svnPass="
            ])
    except getopt.GetoptError as msg:
        usage_and_exit(msg)


    for opt, arg in opts:
        if opt in ("-?", "--help"):
            usage_and_exit()
        elif opt in ("-u", "--ftp-user"):
            user = arg
        elif opt in ("-p", "--ftp-password"):
            password = arg
        elif opt in ("-SU", "--svnUser"):
            svnUser = arg
        elif opt in ("-SP", "--svnPass"):
            svnPass = arg
        elif opt in ("-P", "--ftp-port"):
            try:
               port = int(arg)
            except ValueError as msg:
               usage_and_exit("Invalid value '%s' for --ftp-port." % (arg))
            if port < 1 or port > 65535:
               usage_and_exit("Value for --ftp-port must be a positive integer less than 65536.")
        elif opt in ("-r", "--ftp-remote-dir"):
            remote_base_directory = arg
        elif opt in ("-a", "--access-url"):
            repo_path = arg
        elif opt in ("-s", "--status-file"):
            status_file = os.path.abspath(arg)
        elif opt in ("-d", "--project-directory"):
            project_directory = arg


    if len(args) != 3:
        print(str(args))
        usage_and_exit("host and/or local_repos_path not specified (" + len(args) + ")")

    host = args[0]
    print("args1: " + args[1])
    print("args0: " + args[0])
    print("abspath: " + os.path.abspath(args[1]))
    local_repos_path = os.path.abspath(args[1])
    print('project_dir:',project_directory)

    youngest_revision = int(args[2])


    if status_file == "" : usage_and_exit("No status file specified")



def main():
    global host
    global user
    global password
    global port
    global repo_path
    global local_repos_path
    global status_file
    global project_directory
    global remote_base_directory
    global youngest_revision

    read_args()

    #repository,fs_ptr

    #get youngest revision
    print("local_repos_path: " + local_repos_path)
    print('getting youngest revision...')
    #youngest_revision = fs.youngest_rev(fs_ptr)

    assert youngest_revision, "Unable to lookup youngest revision."
    last_sent_revision = get_last_revision()

    if youngest_revision == last_sent_revision:
        # no need to continue.  we should be up to date.
        print('done, up-to-date')
        return

    if last_sent_revision or youngest_revision < 10:
        # Only compare revisions if the DAT file contains a valid
        # revision number. Otherwise we risk waiting forever while
        # we parse and uploading every revision in the repo in the case
        # where a repository is retroactively configured to sync with ftp.

        pysvn_client = pysvn.Client()
        pysvn_client.callback_get_login = get_login

        rev1 = pysvn.Revision(pysvn.opt_revision_kind.number, last_sent_revision)
        rev2 = pysvn.Revision(pysvn.opt_revision_kind.number, youngest_revision)

        summary = pysvn_client.diff_summarize(repo_path, rev1, repo_path, rev2, True, False)

        print('summary len:',len(summary))
        if len(summary) > 0 :
            print('connecting to %s...' % host)
            ftp = FTPClient(host, user, password)
            print('connected to %s' % host)
            ftp.base_path = remote_base_directory
            print('set remote base directory to %s' % remote_base_directory)

            #iterate through all the differences between revisions
            for change in summary :
                #determine whether the path of the change is relevant to the path that is being sent, and modify the path as appropriate.
                print('change path:',change.path)
                ftp_relative_path = apply_basedir(change.path)
                print('ftp rel path:',ftp_relative_path)

                #only try to sync path if the path is in our project_directory
                if ftp_relative_path != "" :
                    is_file = (change.node_kind == pysvn.node_kind.file)
                    if str(change.summarize_kind) == "delete" :
                        print("deleting: " + ftp_relative_path)
                        try:
                            ftp.delete_path("/" + ftp_relative_path, is_file)
                        except ftplib.error_perm as e:
                            if 'cannot find the' in str(e) or 'not found' in str(e):
                                # Log, but otherwise ignore path-not-found errors
                                # when deleting, since it's not a disaster if the file
                                # we want to delete is already gone.
                                logging.error(captureErrorMessage(e))
                            else:
                                raise
                    elif str(change.summarize_kind) == "added" or str(change.summarize_kind) == "modified" :
                        local_file = ""
                        if is_file :
                            local_file = svn_export_temp(pysvn_client, repo_path, rev2, change.path)
                        print("uploading file: " + ftp_relative_path)
                        ftp.upload_path("/" + ftp_relative_path, is_file, local_file)
                        if is_file :
                            os.remove(local_file)
                    elif str(change.summarize_kind) == "normal" :
                        print("skipping 'normal' element: " + ftp_relative_path)
                    else :
                        raise str("Unknown change summarize kind: " + str(change.summarize_kind) + ", path: " + ftp_relative_path)
            ftp.close()

    #write back the last revision that was synced
    print("writing last revision: " + str(youngest_revision))
    set_last_revision(youngest_revision)     # todo: undo

def get_login(a,b,c,d):
    #arguments don't matter, we're always going to return the same thing
    try:
        return True, "svnUsername", "svnPassword", True
    except Exception as e:
        logging.error(e)
        #capture the location of the error
        frame = inspect.currentframe()
        stack_trace = traceback.format_stack(frame)
        logging.debug(stack_trace)
        #end capture
        sys.exit(1)

#functions for persisting the last successfully synced revision
def get_last_revision():
    if os.path.isfile(status_file) :
        f=open(status_file, 'r')
        line = f.readline()
        f.close()
        try: i = int(line)
        except ValueError:
            i = 0
    else:
        i = 0
    f = open(status_file, 'w')
    f.write(str(i))
    f.close()
    return i

def set_last_revision(rev) :
    f = open(status_file, 'w')
    f.write(str(rev))
    f.close()


#augmented ftp client class that can work off a base directory
class FTPClient(ftplib.FTP) :
    def __init__(self, host, username, password) :
        self.base_path = ""
        self.current_path = ""
        ftplib.FTP.__init__(self, host, username, password)

    def cwd(self, path) :
        debug_path = path
        if self.current_path == "" :
            self.current_path = self.pwd()
            print("pwd: " + self.current_path)

        if not os.path.isabs(path) :
            debug_path = self.base_path + "<" + path
            path = os.path.join(self.current_path, path)
        elif self.base_path != "" :
            debug_path = self.base_path + ">" + path.lstrip("/")
            path = os.path.join(self.base_path, path.lstrip("/"))
        path = os.path.normpath(path)

        #by this point the path should be absolute.
        if path != self.current_path :
            print("change from " + self.current_path + " to " + debug_path)
            ftplib.FTP.cwd(self, path)
            self.current_path = path
        else :
            print("staying put : " + self.current_path)

    def cd_or_create(self, path) :
        assert os.path.isabs(path), "absolute path expected (" + path + ")"
        try: self.cwd(path)
        except ftplib.error_perm as e: 
            for folder in path.split('/'): 
                if folder == "" :
                    self.cwd("/")
                    continue

                try: self.cwd(folder)
                except: 
                    print("mkd: (" + path + "):" + folder)
                    self.mkd(folder)
                    self.cwd(folder)

    def upload_path(self, path, is_file, local_path) :
        if is_file:
            (path, filename) = os.path.split(path)
            self.cd_or_create(path)

            # Use read-binary to avoid universal newline support from stripping CR characters.
            f = open(local_path, 'rb')

            self.storbinary("STOR " + filename, f)

            f.close()
        else:
            self.cd_or_create(path)

    def delete_path(self, path, is_file) :
        (path, filename) = os.path.split(path)
        print("trying to delete: " + path + ", " + filename)
        self.cwd(path)
        try:
            if is_file :
                self.delete(filename)
            else:
                self.delete_path_recursive(filename)
        except ftplib.error_perm as e:
            if 'The system cannot find the' in str(e) or '550 File not found' in str(e):
                # Log, but otherwise ignore path-not-found errors
                # when deleting, since it's not a disaster if the file
                # we want to delete is already gone.
                logging.error(captureErrorMessage(e))
            else:
                raise

    def delete_path_recursive(self, path):
        if path == "/" :
            raise "WARNING: trying to delete '/'!"
        for node in self.nlst(path) :
            if node == path :
                #it's a file.  delete and return
                self.delete(path)
                return
            if node != "." and node != ".." :
                self.delete_path_recursive(os.path.join(path, node))
        try: self.rmd(path)
        except ftplib.error_perm as msg :
            sys.stderr.write("Error deleting directory " + os.path.join(self.current_path, path) + " : " + str(msg))


# apply the project_directory setting
def apply_basedir(path) :
    #remove any leading stuff (in this case, "trunk/") and decide whether file should be propagated
    if not path.startswith(project_directory) :
        return ""
    return path.replace(project_directory, "", 1)

def svn_export_temp(pysvn_client, base_path, rev, path) :
    # Causes access denied error. Couldn't deduce Windows-perm issue.
    # It's possible Python isn't garbage-collecting the open file-handle in time for pysvn to re-open it.
    # Regardless, just generating a simple filename seems to work.
    #(fd, dest_path) = tempfile.mkstemp()
    dest_path = tmpName = '%s.tmp' % __file__

    exportPath = os.path.join(base_path, path).replace('\\','/')
    print('exporting %s to %s' % (exportPath, dest_path))
    pysvn_client.export( exportPath,
            dest_path,
            force=False,
            revision=rev,
            native_eol=None,
            ignore_externals=False,
            recurse=True,
            peg_revision=rev )

    return dest_path    

if __name__ == "__main__":
    logging.info('svnftp.start')
    try:
        main()
        logging.info('svnftp.done')
    except Exception as e:
        # capture the location of the error for debug purposes
        frame = inspect.currentframe()
        stack_trace = traceback.format_stack(frame)
        logging.debug(stack_trace[:-1])
        print(stack_trace)
        # end capture
        error_text = '\nFATAL EXCEPTION!!!\n'+captureErrorMessage(e)
        subject = "ALERT: SVN2FTP Error"
        message = """An Error occurred while trying to FTP an SVN commit.

repo_path = %(repo_path)s\n
local_repos_path = %(local_repos_path)s\n
project_directory = %(project_directory)s\n
remote_base_directory = %(remote_base_directory)s\n
error_text = %(error_text)s
        """ % globals()
        email(toAddrs, message, subject)
        logging.error(e)

附注/免责声明:

  • 我基本上没有蟒蛇训练,所以我正在学习,花大量的时间阅读文档来解决问题。
  • get_login的主体位于一个try块中,因为我收到了奇怪的错误,说callback_get_login中有一个未处理的异常。从来不知道为什么,但现在看起来很好。让睡着的狗撒谎,对吧?
  • get_login的用户名和密码目前都是硬编码的(但正确的),只是为了消除变量,并尽可能少地一次更改。(我将svnuser和svnpass参数添加到现有的参数解析中。)

所以那就是我在的地方我不明白为什么它没有在svn2ftp.out.log上打印任何东西。如果您想知道,svn2ftp.debug.log中的这些失败尝试之一的输出是:

代码语言:javascript
复制
2012-09-06 15:18:12,496 INFO svnftp.start
2012-09-06 15:18:12,496 INFO svnftp.done

成功的跑步也没什么不同。所以没有什么有用的记录。

我迷路了。我在这件事上已经走投无路了,不知道从这里往哪里走。有什么想法吗?

EN

回答 1

Stack Overflow用户

发布于 2012-09-07 15:06:50

看起来,您正在覆盖日志记录级别。尝试将两者都设置为调试并查看发生了什么。

代码语言:javascript
复制
import sys, os
import logging
logging.basicConfig(
    level=logging.DEBUG,  # DEBUG here
    format='%(asctime)s %(levelname)s %(message)s',
    filename='svn2ftp.debug.log',
    filemode='a'
)
console = logging.StreamHandler()
console.setLevel(logging.ERROR) # ERROR here
logging.getLogger('').addHandler(console)

另外,你在一些地方打印,在另一些地方登录。我不确定日志库是否会自动将sys.stdout重定向到日志记录控制台。我会将所有print语句转换为日志语句以保持一致。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/12308036

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档