psf / requests

A simple, yet elegant, HTTP library.
https://requests.readthedocs.io/en/latest/
Apache License 2.0
52k stars 9.29k forks source link

C Runtime Error R6034 with py2exe #1317

Closed fedeanna closed 11 years ago

fedeanna commented 11 years ago

I'm working in a Com Server DLL where I use requests to make POST a multipart form along with some XML file. Localy, it works great and with no error. I can call the COM object methods and it returns me what I'm expeting to.

Now, when I use py2exe to build a dist version of the app, and try to register the DLL Com Server with regsvr32, I get the following error: "Runtime Error! ... R6034 An application has made an attempt to load the C runtime library incorrecly. ..." Also, I've noticed that if I remove any reference to the requests library in my python code, I don't get this issue (obiously, I've lost the main functionality of the module).

sigmavirus24 commented 11 years ago

First, do you have the manifest properly set up? I have no experience with py2exe but this question at StackOverflow seems to indicate this can solve your issue.

On the other hand, what version of requests are you using?

sigmavirus24 commented 11 years ago

For what it's worth other similar questions at StackOverflow indicate similar solutions:

This other one also suggests you might be missing a dependency (which could be requests if in fact removing it solves your issue):

fedeanna commented 11 years ago

I tried the manifest solution in every possible flavor. Also installed the runtime from the microsoft redistributable installer. No effects whatsoever. I'm possitively sure that it's got nothing to do with the runtime itself.

Made virtual machines with W7 and XP, for testing purposes. I can register the Com server with regsvr32, but the error occurs when calling the method. However, if I comment the line where I make the POST with the request lib, or if I call another method where request is not involved, no errors occurs and everything seems to be working fine. Of course, the key functionality of the COM is lost, since everything revolves around the request lib. And I sincerely don't want to use the primitive urllib lib...

My conclusion is the same as yours: seems to be something with py2exe and the request lib. Some dependency has to be missing. Although I can't figure what or why, since py2exe seems to be packing everything... Never before has any problem distributing COM servers with py2exe. I'll keep looking for some answers...

sigmavirus24 commented 11 years ago

Have you tried using the dependency walker?

sigmavirus24 commented 11 years ago

All I can imagine really is that py2exe isn't also packaging the vendored dependencies of requests, i.e., it is packaging requests but not requests.packages, requests.packages.urllib3 or urllib3's vendored dependencies.

dmckeone commented 11 years ago

For what it's worth, I'm using py2exe successfully with requests on Windows. I've seen it working on everything from Win 2000, XP, 2003 Server, Vista, Server 2008, 7 and 8. Although having said that, getting all the dependencies right is a tough go for some things, but I never had issues with requests.

sigmavirus24 commented 11 years ago

Thanks @dmckeone

fedeanna commented 11 years ago

@sigmavirus24 dependency walker gives me two missing dlls, but irrevelant ones. I agree with you, and came to the conclusion that py2exe is having some problems packing requests dependencies. Running py2exe indicates some missing modules, corresponding to requests modules (urllib3, etc.) Using modulefinder I've found where the import of those missing modules are. The issue seems to be that requests is using try statements to test python version (2 or 3) and use the corresponding import. Now, I'm using python 2.7 and this should be working. But it's not. And I cannot figure out why or how to fix it without messing woth requests source code...

@dmckeone : Wich version of Python are you using?

dmckeone commented 11 years ago

@fedeanna I had it working on Python 2.7.3, and now on Python 2.7.4 both 32-bit and 64-bit.

Since I know how much of a hassle this is, here is my setup.py script (It's a little ugly, sorry):

from __future__ import absolute_import, division
import sys

if sys.platform == 'win32':
    from distutils.core import setup
else:
    from setuptools import setup
import babel
import glob
import os

MAIN_SCRIPT = 'myapp.py'
SERVICE_MODULE = 'appservice'

root_path = os.path.dirname(__file__)
package_dir = ""

VISUAL_STUDIO_PATHS = [
    'D:\\Microsoft Visual Studio 9.0',
    'C:\\Program Files (x86)\\Microsoft Visual Studio 9.0',
    'C:\\Program Files\\Microsoft Visual Studio 9.0',
]

if sys.platform == 'darwin':
    import py2app
    sys.argv.insert(1, "py2app")
    sys.argv.insert(2, "--iconfile")
    sys.argv.insert(3, "App.icns")

    extra_options = {
        'setup_requires': ['py2app'],
        'app': [MAIN_SCRIPT],
        'options': {
          'py2app': {
            'optimize': 2,
            'plist': {
               'LSUIElement': True,
               'LSBackgroundOnly': True,
               'CFBundleIdentifier': "com.aplace.myapp,
               'NSHumanReadableCopyright': "Copyright A Place Ltd. 2013",
            },
          },
        },
    }

    extra_data_files = [
        ("babel", glob.glob(os.path.join(os.path.dirname(babel.__file__), "localedata")))
    ]

    dist_path = "dist"
    arch = "universal"
    package_dir = os.path.join(root_path, dist_path, "MyApp.app", "Contents")

elif sys.platform == 'win32':
    import py2exe
    sys.argv.insert(1, "py2exe")

    # Get a good visual studio path
    visual_studio_path = None
    for path in VISUAL_STUDIO_PATHS:
        visual_studio_path = path
        if os.path.exists(visual_studio_path):
            break
    if not visual_studio_path:
        raise ValueError(u"Unable to locate visual studio path.  Tried:\n{}".format(u"\n".join(VISUAL_STUDIO_PATHS)))

    max_size = u"{:x}".format(sys.maxsize)
    if max_size == u"7fffffffffffffff":
        # 64-bit systems
        visual_studio_path = os.path.join(visual_studio_path, r'VC\redist\amd64\Microsoft.VC90.CRT\*.*')
        arch = "64"
        dist_path = "dist64"
    elif max_size == u"7fffffff":
        # 32-bit systems
        visual_studio_path = os.path.join(visual_studio_path, r'VC\redist\x86\Microsoft.VC90.CRT\*.*')
        arch = "32"
        dist_path = "dist32"
    else:
        raise ValueError(u"Unknown system architecture.  sys.maxsize == {}".format(max_size))

    includes = ["win32com" ,"win32service", "win32serviceutil", "win32event"]
    excludes = ['tcl', 'Tkconstants', 'Tkinter', 'distutils']
    packages = []
    dll_excludes = ['tcl84.dll', 'tk84.dll', 'SHFOLDER.dll', 'Secur32.dll', 'MPR.dll', 'CRYPT32.dll', 'KERNELBASE.dll',
                    'MSASN1.dll', 'API-MS-Win-Core-LocalRegistry-L1-1-0','API-MS-Win-Core-ErrorHandling-L1-1-0.dll',
                    'API-MS-Win-Core-LibraryLoader-L1-1-0.dll', 'API-MS-Win-Core-Misc-L1-1-0.dll',
                    'API-MS-Win-Core-ProcessThreads-L1-1-0.dll', 'API-MS-Win-Core-Profile-L1-1-0.dll',
                    'API-MS-Win-Core-String-L1-1-0.dll', 'API-MS-Win-Core-SysInfo-L1-1-0.dll',
                    'API-MS-Win-Security-Base-L1-1-0.dll'
    ]

    extra_options = dict(
        service=[{'modules':[SERVICE_MODULE], 'cmdline_style':'pywin32'}],
        console=[MAIN_SCRIPT],
        options={'py2exe': {
                    'compressed': 2,
                    'optimize': 2,
                    'excludes': excludes,
                    'dll_excludes': dll_excludes,
                    "bundle_files": 3,
                    "dist_dir": dist_path,
                    "xref": False,
                    "skip_archive": False,
                    "ascii": False,
                    }
                },
        zipfile="lib/shared.zip",
    )

    extra_data_files = [
        ("babel\\localedata", glob.glob(os.path.join(os.path.dirname(babel.__file__), "localedata", "*.*"))),
        ("Microsoft.VC90.CRT", glob.glob(visual_studio_path)),
    ]

    package_dir = os.path.join(root_path, dist_path)

else:
    raise OSError (u"Unknown Platform")

setup(
    name="My App",
    version=0.1,
    author="David McKeone",
    author_email="notmyemail@aplace.com",
    description="My App",
    url="http://www.aplace.com",

    data_files=extra_data_files,

    **extra_options
)

It freezes two .exe files on Windows (a service and a command line program), as well as an .app bundle on the Mac with py2app. Hopefully that can help you par it down to what makes it work. This app is a Flask app that uses babel, so if you aren't using babel you can probably get rid of some of that stuff.

Note that I am missing a copy of the requests cacert.pem file, because I'm not using SSL requests. If you did need verified SSL then you'd need to do something similar to what I did for babel, and use the requests.get('http://127.0.0.1', cert=path_to_cacert_pem) or monkey-patch requests.utils.DEFAULT_CA_BUNDLE_PATH to be your cacert path

I hope that helps, but if it doesn't then your next stop should likely be the py2exe forums, since I'm fairly certain it's not a Requests issue.