I am making an app in python, which is able to open different file types. This code in running fine on eclipse while passing filename which I want to open and configuration file as arguments respectively. Now I converted this into application by using py2app. So, the issue is how to deal with arguments, as different file types need to be open through app and this app also needs configuration file while processing. Is there any different methods available for making app which can be installed on mac osx.
Have you had a look at py2app? Its pretty much py2exe for Mac OSX and creates a standalone app. You can handle the logger and configuration files pretty easily and keep your app nice and simple to distribute.
I usually store logger files in the users home directory as a hidden file - a lot of Mac OSX applications do this - i'm not sure if its officially the way to go but it works. The home directory is guaranteed to exist and you won't get permission errors like you would when you write to other random folders or to location inside the app bundle.
You can do this and keep your code cross platform buy opening files like this
import platform
import os
if platform.system() == 'Darwin': # We're on a mac
# Store the saved rates in the users home dir - note the dot at the start
# of the filename to keep the file hidden
home = os.path.expanduser("~")
filename = os.path.join(home, '.myHiddenLogger')
else: # We're on another platform, create whatever filename you were using before
filename = 'myLogger'
As for the configuration file, this answer here tells you how to bundle a resource file in with you're app. If you want to write stuff to this configuration file and save it for next time, you'll have to store it as a hidden file like the logger - this will stop the app from crashing due to permission errors when it tried to write to a file inside the app bundle.
Related
I am using PyInstaller to bundle my multi-module Python app into a one-file exe. The entry point for this app is a module extending win32serviceutil.ServiceFramework -- so this is meant to run as a Windows service. Problem arises when I try to furnish a user configurable logger ini file with this app. In my main module, I set up the logger thus,
log_file_path = path.join(path.dirname(path.abspath(__file__)), 'logging.conf')
logging.config.fileConfig(log_file_path)
My PyInstaller command is the following:
pyinstaller -F <main-file>.py -n <exe-name> --hidden-import=win32timezone --add-data "logging.conf;."
Once packaged, I install the produced exe as a service and it successfully registers as a Windows service. However when I attempt to start it, it fails.
The interesting bit is that an empty log file is created in my configured location. So this implies that 1) The application did read my config file, and 2) There's no permissions issue here. Has anyone tried to set up something like this that could help shed light on what I might be missing?
When using the one-file option with PyInstaller, we cannot validly use the __file__ variable in our code to identify the bundled app or its location. Instead, PyInstaller sets special system variables at runtime, like sys._MEIPASS and sys.executable; which specify the temporary folder created by the bootloader to run the app, and location of the frozen executable (the bootloader), respectively.
Once I changed my path manipulation to use these variables to locate the log config file, the file is successfully read and the service works.
My question is essentially, "How should I structure the files and folders of my frozen, deployed Python-based Windows application." To understand my situation, here's some background:
I'm building a desktop application with Python 2.7 for my workplace. It is a GUI-based application built on PyQt. I am building the application with Esky which is a cross-platform freezing and updating framework. Esky basically wraps/calls py2exe, py2app, bb_freeze, or whatever tool you have installed that is appropriate for the current platform. Esky creates a zipped package that looks like this:
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
These zipped packages can then be placed on a file server which Esky looks to for updates. A number of methods are provided for managing updates including a very simple auto_update(). When an update occurs, the appname-X.Y.platform folder is essentially replaced with the next version folder... so the myApp.0.1.win32 folder is replaced by a myApp.0.2.win32 folder.
The other aspect of background you should know is that I am distributing the application to my coworkers, who do not have Python installed. I'm not distributing a Python package or library, I'm deploying a desktop application (my coworkers don't particularly care what it's written in, just that it works). I've built an Inno installer which installs the application, provides an uninstaller, and various shortcuts. Because everyone on the team has essentially the same Windows 7 64-bit environment, I'm pretty safe building for just that platform.
So, back to the issue of structure. I've read guides that recommend a certain format for a project skeleton, such as Learn Python the Hard Way, Exercise 46 or the Hitchhiker's Guide to Packaging. However these guides are oriented toward Python package developers, not compiled application developers.
I've also run into problems with Esky's appname-X.Y.platform folder, since it changes names every time the program is updated (to reflect the version number). Because I want some shortcuts in the Start Menu to always refer to documentation, changelog, etc, I have the installer place some of those files under the appdata folder. When the program updates, I have some code to check for newer versions of those files I want to be externally "visible" and copy the newer versions out of the appname-X.Y.platform folder and overwrite the copies in the appdata folder. I then also needed a means of storing persistent user settings, so the program generates and uses an appdata\settings folder (otherwise the settings would be wiped with each update).
Should I continue the process of having the application push new files out to the appdata folder post-update? Should I build my own structure of Docs, Examples, Settings, etc. and let the program populate those folders with newer files whenever necessary? Should I attempt to alter or take better advantage of Esky's behavior to better fit my usage? Perhaps I should rework my application to be destributable as both a Python package and an end-user application?
This question relates to this one about static files with Esky, this one about Python deployed application structure, and numerous generic questions about Python project structure which don't specifically address using Esky. Some videos discussing Esky are also available here and here.
I'm seeking recommendations for "best practice" methods to handle these challenges. If this doesn't fit the StackOverflow Question format, I'll gladly attempt to reword or narrow the focus of my question.
So here's my solution to the problem mentioned about about making files available to shortcuts at a static location despite the fact that Esky's auto-updating changes the name of my application folder every update. The function below I have within a class definition for a QMainWindow.
Logging statements could be replaced with print statements if your application doesn't use the logging module, though I highly recommend logging, especially if deploying a standalone application like this.
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)
Also related to this, when dealing with user settings (which currently is only a history.txt file used to populate a recent files list) I have a settings folder under appdata but outside the application folder so that settings aren't lost each update. I may make similar folders for documentation and icons.
I am making an app in python. Using python 2.7 on mac osx 10.8.5. I converted python code into an app using py2app. Then I converted myapp.app into package myapp.pkg using Packages.
I am using a configuration file which I kept in Contents-->Resources-->config.cfg. config.cfg file contains data which is necessary for running of app.
My issue is how to pass serverIP and serverPort to config.cfg file while installing myapp through myapp.pkg i.e. through Packages. My config.cfg file is like key-value pair.I am using config.parsar to read config.cfg file.
[KMS]
serverIP1 =
serverPort1 =
serverIP2 =
serverPort2 =
I need to pass these value to config.cfg file while installing package.The installer should ask these values and the user should input these values.
A possible solution would be to write a packagemaker plugin that will ask these details from the user and then save it to some file. Then during installation, from a post-install script, you can read this file and modify your config file.
However, it is bad idea to keep such config files inside the app file. A better place for such files is ~/Library/Preferences or ~/Library/Application Support.
Tutorials for creating packagemaker plugin: (Requires Cocoa knowledge)
http://www.mactech.com/articles/mactech/Vol.25/25.06/InstallerPlugins/index.html
http://s.sudre.free.fr/Stuff/Installer/Installer_Plugins/
I have a python program that must work on Windows and Linux. There are some configuration options I normally store in a file, in a subdirectory of the program's directory.
For Windows, I converted it to exe and created an Installer for it. And now I have the problem of dealing with the config file.
What is the best place to save the configuration file? I have read that for Windows os.environ['APPDATA']+'myAppName' is the path that must be used. Is it correct? Is it standard? Will it work in all versions of Windows at least from XP (and at least in English and Spanish)?
PD: I am not interested in using ConfigParser. Config file is in my own format and I have working code for reading/writing from it.
Storing settings in the user directory is usually a good idea.
These days, you should probably use something like the appdirs library to find a good path to store your configuration in.
Under most Unices, just store a (preferably dot-prefixed) file in the home directory. Under OS X, you'd want to create a directory for your application in the user's Library folder, and store your files there. Under Windows, APPDATA is a good place to build a directory in for your application. It should work on all Windows localizations, and it looks like it was also available in Windows XP.
platformdirs
There is a better solution now ... better than speculating about what is best on each platform, better than environment variables which may or may not be defined, and even better than appdirs: platformdirs (GitHub, PyPI Snyk).
import platformdirs
appname = 'OurGreatApp'
print(f"User config files should be stored in {platformdirs.user_config_dir(appname)}")
On Linux, it is common to store the configuration file in the users home directory, for instance ~/.myprogramrc. On windows Vista and up, users have a home directory as well (/Users/username) and a would recommend storing your settings there in a subfolder (/Users/useranem/myprogram). Storing the settings in the application folder will generate UAC warnings.
On Windows XP, users do not have a home folder. Some programs make the choice of putting configuration in the 'My Documents' folder which I guess is as good a place as any.
I am really new to packaging.
I have developed a music player using pyqt in ubuntu.
It has a gui and it uses sqlite database.
I have looked at distutil.
What I understood is how to place modules and scripts in right place.
What I don't understand is how to set paths for database, config files & log files.
How do I achieve it the way other applications do it in ubuntu by maintainig all this data in '.application_name' folder under home for a user ?
Can anyone suggest a good example application to learn from or point in some direction?
You can use QDir.home() to get the absolute path to a user's home path. You can use this path when generating/accessing your database, config files and log files. For example, on first startup you can do something like:
filePath = QDir.home() + "/.application_name"
if not QDir.exists(filepath)
QDir.mkdir(filepath)
Then you can use filePath when reading/writing to the files from there on out.