2013-12-09 47 views
3

我的问题基本上是“我应该如何构建我的冷冻,部署的基于Python的Windows应用程序的文件和文件夹”。为了理解我的情况,这里有一些背景:Esky的Python项目结构

我正在用我的工作场所的Python 2.7构建一个桌面应用程序。这是一个基于PyQt构建的基于GUI的应用程序。我正在用Esky构建应用程序,这是一个跨平台的冻结和更新框架。 Esky基本上会打包/调用py2exe,py2app,bb_freeze或适用于当前平台的已安装的任何工具。 ESKY创建一个压缩包,看起来像这样:

prog.exe      - esky bootstrapping executable 
appdata/      - container for all the esky magic 
    appname-X.Y.platform/  - specific version of the application 
    prog.exe     - executable(s) as produced by freezer module 
    library.zip    - pure-python frozen modules 
    pythonXY.dll    - python DLL 
    esky-files/    - esky control files 
     bootstrap/    - files not yet moved into bootstrapping env 
     bootstrap-manifest.txt - list of files expected in bootstrap env 
     lockfile.txt   - lockfile to block removal of in-use versions 
     ...other deps... 
    updates/     - work area for fetching/unpacking updates 

这些压缩包可以被放置其中ESKY看起来更新文件服务器上。提供了许多管理更新的方法,包括一个非常简单的auto_update()。当更新发生时,appname-X.Y.platform文件夹基本上被替换为下一个版本文件夹...所以myApp.0.1.win32文件夹被替换为myApp.0.2.win32文件夹。

你应该知道的背景的另一方面是我将应用程序分发给我的同事,他们没有安装Python。我没有发布一个Python包或库,我正在部署一个桌面应用程序(我的同事并不特别在意它写的是什么,只是它的工作原理)。我已经构建了一个安装应用程序的Inno安装程序,提供了一个卸载程序和各种快捷方式。由于团队中的每个人都具有基本相同的Windows 7 64位环境,因此我非常安全地构建该平台。

那么,回到结构问题。我已阅读为项目框架推荐某种格式的指南,例如Learn Python the Hard Way, Exercise 46Hitchhiker's Guide to Packaging。但是,这些指南面向Python包开发人员,而不是编译应用程序开发人员。

我也碰到过Esky的appname-X.Y.platform文件夹的问题,因为每次程序更新时都会更改名称(以反映版本号)。因为我想在开始菜单中使用一些快捷方式来总是引用文档,更新日志等,所以我让安装程序将这些文件中的一部分放在appdata文件夹下。当程序更新时,我有一些代码来检查我希望在外部“可见”的那些文件的较新版本,并将新版本从appname-X.Y.platform文件夹中复制出来,并覆盖appdata文件夹中的副本。然后,我还需要一种存储持久用户设置的方法,以便程序生成并使用appdata \ settings文件夹(否则每次更新都会擦除设置)。

我应该继续让应用程序将新文件推送到appdata文件夹更新后的过程吗?我是否应该构建自己的文档,示例,设置等结构,并让程序在必要时使用新文件填充这些文件夹?我是否应该尝试改变或更好地利用Esky的行为以更好地适应我的使用习惯?也许我应该将我的应用程序重写为可以作为Python包和最终用户应用程序进行解析?

这个问题涉及到this one about static files with Esky,this one about Python deployed application structure,以及许多关于Python项目结构的一般问题,这些问题没有专门用到Esky。一些视频讨论Esky也可用herehere

我正在寻求针对“最佳实践”方法来处理这些挑战的建议。如果这不适合StackOverflow问题格式,我会很乐意尝试重新说明或缩小我的问题的焦点。

回答

2

因此,这里是我的解决方案提及约尽管ESKY的自动更新变化,每更新我的应用程序文件夹的名称,这一事实在静态位置使提供给快捷方式文件的问题。下面的函数我有一个类定义为QMainWindow的范围内。

日志报表可以用打印语句来代替,如果您的应用程序不使用日志模块,虽然我极力推荐的日志记录,尤其是如果部署这样一个独立的应用程序。

import os 
import shutil 
import logging 

def push_updated_files(self): 
    """ 
    Manually push auto-updated files from the application folder up to the appdata folder 
    This enables shortcuts and other features on the computer to point to these files since the application 
     directory name changes with each update. 
    """ 
    logger = logging.getLogger(__name__) 

    #Verify whether running script or frozen/deployed application 
    if getattr(sys, 'frozen', False): 
     logger.info("Verifying Application Integrity...") 

     #Files which should by copied to appdata directory to be easily referenced by shortcuts, etc. 
     data_files = ['main.ico', 
         'uninstall.ico', 
         'ReadMe.txt', 
         'changelog.txt', 
         'WhatsNew.txt', 
         'copyright.txt', 
         'Documentation.pdf'] 

     logger.debug(" App Path: {0}".format(self._app_path)) 

     #Get application top directory 
     logger.debug(" AppData Directory: {0}".format(self._appdata_path)) 

     #Get application internal file path 
     for f in data_files: 
      a_file = f 
      int_path = os.path.join(self._app_path, a_file) 
      logger.debug(" Internal File Path: {0}".format(int_path)) 

      #Get file's creation time 
      mtime_int = os.stat(int_path).st_mtime 
      logger.debug(" Internal File Modified Time: {0}".format(time.ctime(mtime_int))) 

      #Get external file path 
      ext_path = os.path.join(self._appdata_path, a_file) 
      if os.path.exists(ext_path): 
       mtime_ext = os.stat(ext_path).st_mtime 
       logger.debug(" External File Modified Time: {0}".format(time.ctime(mtime_ext))) 

       if mtime_int > mtime_ext: 
        logger.debug(" Replacing external file with new file...") 
        try: 
         os.remove(ext_path) 
         shutil.copy(int_path, ext_path) 
        except Exception, e: 
         logger.error(" Failed to replace the external file...", exc_info=True) 
       else: 
        logger.debug(" External file is newer than internal file - all is well.") 
      else: 
       logger.debug(" Copying file to appdata to be externally accessible") 
       shutil.copy(int_path, ext_path) 

也与此有关,与用户设置处理(目前仅用于填充最近使用的文件列表中的文件history.txt)时,我有以下应用程序数据,但应用程序文件夹之外的设置文件夹中,这样的设置不会丢失每次更新。我可就文件和图标类似文件夹。