I'm having troubles with praw, cx_freeze, pyside and requests, before freezing everything works fine, but when i freeze this happens, something with requests goes wrong i think:
http://pastie.org/10614254
This is the project that i'm working with: https://github.com/MrCappuccino/WallDit-QT
This is my setup.py: https://gist.github.com/MrCappuccino/0f1b0571d29d47a95895
import sys
import cx_Freeze
import PySide
import praw
import requests.certs
from cx_Freeze import setup, Executable
exe = Executable(
script="WallDit_QT.py",
base="Win32GUI",
targetName="WallDit_QT.exe"
)
#includefiles = ['README.txt', 'CHANGELOG.txt', 'helpers\uncompress\unRAR.exe', , 'helpers\uncompress\unzip.exe']
#build_exe_options = {"packages": ["os"], "includefiles": ['README.txt', 'CHANGELOG.txt']}
setup(name = 'WallDit_QT',
version = '1.0',
author = 'Disco Dolan',
description ='Set your wallpaper interactively!',
executables = [exe],
options = {'build.exe': {"include_files":['cacert.pem', 'praw.ini', 'README.md']}},
requires = ['PySide', 'cx_Freeze', 'praw', 'shutil', 'requests']
)
could anybody help out?
I have tried adding cacert.pem, to no avail, at this point i have no more ideas
For some frozen applications, you have to set the the cacert (or external data in general) path inside the frozen applications.
Setup.py Section
You first need to include it in your build options and manually specify the installation directory. This is the only part that goes inside the setup.py:
# notice how I say the folder the certificate is installed
{"include_files":[(requests.certs.where(),'cacert.pem')]}
In your case, this produces the following setup file:
import requests
import sys
# more imports
setup(name = 'WallDit_QT',
version = '1.0',
author = 'Disco Dolan',
description ='Set your wallpaper interactively!',
executables = [exe],
options = {
'build.exe': {
"include_files": [
(requests.certs.where(),'cacert.pem'),
'praw.ini',
'README.md'
]
}
},
requires = ['PySide', 'cx_Freeze', 'praw', 'shutil', 'requests']
)
Application Section
You then need to get the certificate path at runtime inside the frozen application.
For PyInstaller, a path is defined at runtime to the data directory called _MEIPASS (which can be gotten from sys._MEIPASS), allowing you to access all the data required for the application. In the case of cacert.pem, the path would be determined as follows:
cacertpath = os.path.join(sys._MEIPASS, "cacert.pem")
For cx_Freeze, the path can be determined from the path of the installation and joining it with the data desired. Here, we get the path as follows:
cacertpath = os.path.join(datadir, 'cacert.pem')
You can get the data directory easily for frozen applications with the following:
datadir = os.path.dirname(sys.executable)
(Please note that this won't work with a non-frozen application, so to ensure it works for both frozen and non-frozen applications, Cx_Freeze recommends you code it as follows):
def find_data_file(filename):
if getattr(sys, 'frozen', False):
# The application is frozen
datadir = os.path.dirname(sys.executable)
else:
# The application is not frozen
# Change this bit to match where you store your data files:
datadir = os.path.dirname(__file__)
return os.path.join(datadir, filename)
You then include this path all your requests module GET and POST requests as follows:
request.get(url, headers=headers, verify=cacertpath)
Example 1
An example code snippet would be as follows:
# load modules
import os
import sys
import requests
# define our path finder
def find_data_file(filename):
if getattr(sys, 'frozen', False):
# The application is frozen
datadir = os.path.dirname(sys.executable)
else:
# The application is not frozen
# Change this bit to match where you store your data files:
datadir = os.path.dirname(__file__)
return os.path.join(datadir, filename)
# get our cacert path and post our GET request
cacertpath = find_data_file('cacert.pem')
r = requests.get('https://api.github.com/events', verify=cacertpath)
# print the text from the request
print(r.text)
Example 2
You can also tell requests where to find the certificate in the future by doing the following:
os.environ["REQUESTS_CA_BUNDLE"] = cacertpath
In this case, we would do the following. The advantage here is that the cacertpath does not to be explicitly defined in every module (or imported from another module) and can be defined in the environment.
import os
import sys
import requests
def find_data_file(filename):
if getattr(sys, 'frozen', False):
# The application is frozen
datadir = os.path.dirname(sys.executable)
else:
# The application is not frozen
# Change this bit to match where you store your data files:
datadir = os.path.dirname(__file__)
return os.path.join(datadir, filename)
cacertpath = find_data_file('cacert.pem')
os.environ["REQUESTS_CA_BUNDLE"] = cacertpath
r = requests.get('https://api.github.com/events')
r.text
Related
I have a custom PyPi package. It is installed under Pyhon\Python38\Lib\site-packages\myCustomPackage.
In the __init__ code for myCustomPackage, I perform a few different directory operations, which failed to find the correct files and directories which reside in the Pyhon\Python38\Lib\site-packages\myCustomPackage folder.
I looked at the output of os.getcwd() and it showed the cwd to be C:\Users\TestUser, which is the root Windows user folder.
I would like the root folder to be the myCustomPackage folder.
For example, the file \myCustomPackage\__init__.py would contain
import os
class myCustomPackage():
def __init__(self):
print(os.getcwd())
If I run:
from myCustomPackage import myCustomPackage
theInstance = myCustomPackage()
The output is:
C:\Users\TestUser
How can I change that to be C:\Users\TestUser\AppData\Local\Programs\Python\Python38\Lib\site-packages\myCustomPackage?
Note : I would want it to be dynamic. No hard coding, in case the python version changes or the Windows user changes.
To get the directory path of the current module, you can use the built-in __file__.
To set the cwd to the module directory, use:
import os
import sys
from pathlib import Path
class myCustomPackage():
def __init__(self):
module_directory = Path(__file__).parent
os.chdir(module_directory)
print(os.getcwd())
My solution was the following function:
import site
import os
import traceback
def changeCWD(self):
try:
sitePackages = site.getsitepackages()
site_packages_dir = sitePackages[1]
top_module_dir = site_packages_dir + os.path.sep + "myCustomPackage"
os.chdir(top_module_dir)
return True
except:
self.myLogger.error("An error occurred in changeCWD")
tb = traceback.format_exc()
self.myLogger.exception(tb)
return False
I have been following a tutorial on how to make a cx_freeze setup for pygame folders. Here is what I have come up with...
import cx_Freeze
executables = [cx_Freeze.Executable("mainGame - Copy.py")]
cx_Freeze.setup(
name = "Cave",
version = "1.0",
author = "Owen Pennington",
options = {"build_exe": {"packages":["pygame"], "include_files":["floor_heart.wav"]}},
executables = executables
)
However the rest of my files are in folders. Then there are folders inside these folders. For example I have a folder (path directory) C:CaveGame\Sprites and this folder holds many other folders, C:CaveGame\Sprites\Floors, C:CaveGame\Sprites\Lava ect... Then I also have a folder C:CaveGame\Music which holds all my music files and sound effects. How can I get these all to work inside the setup???
You just need to include the upper-level directory item in your options dictionary:
setup(name='Widgets Test',
version = '1.0',
description = 'Test of Text-input Widgets',
author = "Fred Nurks",
options = { "build_exe": {"packages":["pygame"], "include_files":["assets/", "music/"] } },
executables = executables
)
The above example will include the files assets/images/blah.png and music/sounds/sproiiiing.ogg along with their correct directories. Everything under that top-level folder is pulled into the lib/.
When you go to load these files, it's necessary to work out the exact path to the files. But the normal method to do this does not work with cxFreeze. Referencing the FAQ at https://cx-freeze.readthedocs.io/en/latest/faq.html ~
if getattr(sys, 'frozen', False):
EXE_LOCATION = os.path.dirname( sys.executable ) # frozen
else:
EXE_LOCATION = os.path.dirname( os.path.realpath( __file__ ) ) # unfrozen
Obviously you need the modules sys and os.path for this.
Then when loading a file, determine the full path with os.path.join:
my_image_filename = os.path.join( EXE_LOCATION, "assets", "images", "image.png" )
image = pygame.image.load( my_image_filename ).convert_alpha()
EDIT: If you're building under Windows, you will need to also include the Visual C runtime: https://cx-freeze.readthedocs.io/en/latest/faq.html#microsoft-visual-c-redistributable-package . Add include_msvcr to options.
options = { "build_exe": { "include_msvcr", "packages":["pygame"] #...
I have the following code:
import datetime as date
import os
import pdfkit
import getpass #Gets me current username
username = getpass.getuser()
path = f"/home/{username}/Data"
relative_path = os.path.relpath(path, os.getcwd())
destination = os.path.join(relative_path, 'data.pdf')
pdfkit.from_url('www.google.com', f'{destination}/data.pdf')
I want the pdf to be saved in windows equivalent of /home/[username]/datafolder. I don't really need to use use linux or mac but for academic reasons i have decided to use the relative path method.
This code makes sense to me but for some reason it is not the directory i want it to be because when i specify the path this way the pdf generator, generates an error.
Error: Unable to write to destination
Exit with code 1, due to unknown error.
I know the error is in the last line of code where i have specified '/relative_path/data.pdf'. Could you please advise how i can resolve this issue?
Update 1:
As suggested by #Matthias I have updated the code but I am still getting the same error
Update 2:
I tried:
from pathlib import Path
destination = Path.home()
try:
os.mkdir(destination\Data)
except OSError as error:
print(error)
But it is still not pointing to the directory Data
Update 3
I know i am getting closer:
import pdfkit
import datetime as date
import calendar
import os.path
import getpass
username = getpass.getuser()
path = f"/home/{username}/Data"
os.makedirs(relative_path, exist_ok=True)
#start = os.getcwd()
relative_path = os.path.relpath(path, os.getcwd())
destination = os.path.join(relative_path, 'data.pdf')
pdfkit.from_url('www.google.com', f'{relative_path}/data.pdf')
At this point the code is executes but the folder Data was not created not am i able to locate data.pdf. I did get sucessful run though:
Loading pages (1/6)
Counting pages (2/6)
Resolving links (4/6)
Loading headers and footers (5/6)
Printing pages (6/6)
Done
Any ideas on how i can get this working correctly? The code does not produce the folder or the file?
Just check by putting
relative_path line before os.makedirs
As below
import pdfkit
import datetime as date
import calendar
import os.path
import getpass
username = getpass.getuser()
#path = os.path.join("home","{username}","Data")
# in case of window you will need to add drive "c:" or "d:" before os.path.sep
path = os.path.join(,"home",username,"Data")
relative_path = os.path.relpath(path, os.getcwd())
os.makedirs(relative_path, exist_ok=True)
#start = os.getcwd()
destination = os.path.join(relative_path, 'data.pdf')
pdfkit.from_url('www.google.com', f'{relative_path}/data.pdf')
Maybe you could change your last line to:
pdfkit.from_url('www.google.com', f'{relative_path}/data.pdf')
in order to get it to save to the home directory.
Perhaps the issue is that the directory doesn't exist. You could use os.makedirs to create the directory, using the exist_ok=True flag in case the directory already exists. Like so:
import datetime as date
import os
import pdfkit
import getpass #Gets me current username
username = getpass.getuser()
path = f"/home/{username}/Data"
os.makedirs(path, exist_ok=True)
pdfkit.from_url('www.google.com', f'{path}/data.pdf')
You can use os.environ. Run this little script on your machine:
import os
for key, value in os.environ.items():
print(key, '-->', value)
and see for yourself what you need exactly. It's portable as well.
Let's say you want to get the path of the user's home directory. You could get it from os.environ['HOME'] and then create the path to the target directory using os.path.join(os.environ['HOME'], 'target_directory_name').
You won't be able to create files in a directory if you don't have the required permissions, though.
User folders in windows are stored in "/Users/{username}/*". I don't know if you are trying to make this compatible for multiple OSs but if you just want to make this run on windows try:
path = f"/Users/{username}/Data"
start = f"/Users/{username}"
Hope it works.:)
Edit:
To get the home directory of a user regardless of OS you could use
from pathlib import Path
home = str(Path.home())
sorry for the late edit.
settings.txt is stored and accessed within the compiled single file app, but it's not being written to. This works prior to Pyinstaller compilation when the file is in the same directory as the script.
The app is compiled from the terminal:
pyinstaller script.spec script.py --windowed --onefile
a.datas is set in the spec file as:
a.datas += [(‘settings.txt’,’/path/to/settings.txt’, "DATA”)]
and the file is read properly within the app:
with open(resource_path('settings.txt'), 'r') as f2
However, the file isn’t updated when attempting to overwrite the file:
def OnExit(self, event):
with open(resource_path('settings.txt'), 'w') as f2:
f2.write('update')
self.Destroy()
resource_path is defined as:
def resource_path(relative_path):
""" Get absolute path to resource, works for dev and for PyInstaller """
try:
# PyInstaller creates a temp folder and stores path in _MEIPASS
base_path = sys._MEIPASS
except Exception:
base_path = os.environ.get("_MEIPASS2", os.path.abspath("."))
return os.path.join(base_path, relative_path)
If you are on Windows, _MEIPASS returns the "short" name for the path in case that any component of it is more than 8 characters long. So, to test that this is the issue, try to make it a one-folder frozen app and then move it in a simple and short path: e.g., C:/test.
If this is the issue, you can workaround the problem by retrieving the long path using something like:
if hasattr(sys, '_MEIPASS'):
import win32api
sys_meipass = win32api.GetLongPathName(sys._MEIPASS)
I wanted to share my solution, which simultaneously addresses many issues with relative paths in general (see function __doc__string).
I have a module named top_level_locator.py, with a modified function module_path as seen in other answers which takes a relative_path.
usage in other .py files:
from top_level_locator import module_path
resource_location = module_path(relative_path = 'resource.ext')
import sys
from pathlib import Path
from inspect import getsourcefile
def module_path(relative_path):
"""
Combine top level path location, in this project app.py folder because it serves as main/entry_point, with user relative path.
NOTE: top_level_locator.py should be in same folder as entry_point.py(/main.py) script
- TEST this with executable
- TEST this without executable
NOTE: care with use of __file__ as it comes with unwarranted side effects when:
- running from IDLE (Python shell), no __file__ attribute
- freezers, e.g. py2exe & pyinstaller do not have __file__ attribute!
NOTE: care with use of sys.argv[0]
- unexpected result when you want current module path and get path where script/executable was run from!
NOTE: care with use of sys.executable
- if non-frozen application/module/script: python/path/python.exe
- else : standalone_application_executable_name.exe
"""
# 0 if this module next to your_entry_point.py (main.py) else += 1 for every directory deeper
n_deep = 1
print('sys.executable:', sys.executable)
print(' sys.argv[0]:', Path(sys.argv[0]).parents[n_deep].absolute() / sys.argv[0])
print(' __file__:', __file__)
print(' getsourcefile:', Path(getsourcefile(lambda:0)).parents[n_deep].absolute())
if hasattr(sys, "frozen"):
# retreive possible longpath if needed from _MEIPASS: import win32api;
# sys_meipass = win32api.GetLongPathName(sys._MEIPASS)
base_path = getattr(sys, '_MEIPASS', Path(sys.executable).parent)
print(' _MEIPASS:', base_path)
return Path(base_path).joinpath(relative_path)
return Path(getsourcefile(lambda:0)).parents[n_deep].absolute().joinpath(relative_path)
if __name__ == '__main__':
module_path()
In non-frozen applications the output will (should) be as such:
sys.executable: C:\Users\<usr_name>\AppData\Local\Programs\Python\Python37\python.exe
sys.argv[0]: c:\Users\<usr_name>\Desktop\<project_name>\<project_code_folder>\app.py
__file__: c:\Users\<usr_name>\Desktop\<project_name>\<project_code_folder>\utils\top_level_locator.py
getsourcefile: c:\Users\<usr_name>\Desktop\<project_name>\<project_code_folder>
In frozen applications:
sys.executable: C:\Users\<usr_name>\Desktop\<project_name>\dist\app.exe
sys.argv[0]: C:\Users\<usr_name>\Desktop\<project_name>\dist\app.exe
__file__: C:\Users\<usr_name>\AppData\Local\Temp\_MEI155562\utils\top_level_locator.pyc
getsourcefile: C:\Users\<usr_name>\Desktop\<project_name>
_MEIPASS: C:\Users\<usr_name>\AppData\Local\Temp\_MEI155562
It is the first time I'm using cx_freeze and I really need help on this, I've been looking everywhere but i can't find an answer.
I don't have any error during the compiling process, but the executable file just doesn't do anything. Could someone please explain me why?
Here is my project structure:
Application
setup.py
application.py (Tkinter app that import 2 functions from cell.py)
logo.jpg
favicon.ico
responsible.xls
modules:
cell.py (all the work is done in this file)
I don't know where to include the file cell.py in setup, for now it's in a file Modules. And i'm not sure if Tkinter should be include or exclude.
Here is the file setup.py:
import sys
from cx_Freeze import setup, Executable
executables = [
Executable("application.py")
]
buildOptions = dict(
compressed = True,
includes = ["sys","re","PIL","ttk","xlrd","xlutils","datetime","string","Tkinter"],
include_files = ["responsible.xls","favicon.ico","logo.jpg"],
excludes = []
path = sys.path + ["modules"]
)
setup(
name = "test",
version = "1.1.1.0",
description = "test",
options = dict(build_exe = buildOptions),
executables = executables
)
You add the directories you want to include with the include_files option.
So this part of your code should be like this:
buildOptions = dict(
compressed = True,
includes = ["sys","re","PIL","ttk","xlrd","xlutils","datetime","string","Tkinter"],
include_files = ["responsible.xls","favicon.ico","logo.jpg", "modules"],
excludes = []
path = sys.path + ["modules"]
)