From: Greg Ercolano <erco@(email surpressed)>
Subject: [Q+A]: Python scripts giving "RuntimeError: Bad magic number in .pyc
   Date: Sat, 15 Aug 2015 13:39:03 -0400
Msg# 2401
View Complete Thread (1 article) | All Threads
Last Next
> We're getting this error from some of the python submits during rendering
> on some of our new machines:
>
> RuntimeError: Bad magic number in .pyc file

    Yes, this happens if you have a mixed network of machines with old and new
    versions of python with incompatible .pyc file formats.

    For instance, mixing python 2.6 and 2.7 can cause this problem.


SIMPLE SOLUTION
===============

    There are several possible solutions (see below), but all have caveats.
    The easiest one is:

       Remove all the *.pyc files from the submit script directory,
       make the directory "read-only" to the render users
       (e.g. writable only to root) so python can't re-create them.

    Python will then operate normally with just the .py files,
    and will work correctly across python versions old + new.

PROBLEM DESCRIPTION (IN DEPTH)
==============================

    When python loads a .py file, it first "compiles" it into
    a byte code format that it can then run.

    As an optimization, someone on the python dev team decided it was a good
    idea to have python try to save the compiled byte code in a .pyc file so
    the next time someone runs the script, it can just load the .pyc file, 
    skipping the byte code compile step.

    The way this was implemented:

	When python is told to run a .py file, it first checks if there's
	already a .pyc version of the file.

	If the .pyc exists and has a newer date stamp than the .py,
	python runs it.

	If the .pyc /doesn't/ exist, or is older than the .py file,
	python falls back to loading the .py file, compiles it to ram,
	and attempts to write the compiled byte code out to a .pyc file
	for next time, then runs the compiled code from ram.

	If it can't write the .pyc file (due to e.g. write permissions),
	it just runs the compiled .py file from ram.

    Trouble is, if the pyc file's byte code format is incompatible,
    then e.g. python 2.6 doesn't do the fallback to the .py file,
    making this "Bad magic" problem fatal to running the script
    (instead of an invisible skipped step)

    This whole issue is raised in this python bug:
    http://bugs.python.org/issue20794

    One of the developers writes:
    ".pyc and .pyo files are in general not compatible across Python versions"

    Great -- an easy situation to have if you have a mix of machines with
    different default python versions all running the same .py script,
    and no easy way to disable creation of.pyc files.

    The .pyc file is supposed to be an optional optimization.
    But python (e.g. 2.6) doesn't fall back to loading the .py file
    if the .pyc exists but can't be loaded due to this error.

ALTERNATE SOLUTIONS TO THIS PROBLEM
===================================

    There are other ways to avoid this problem without making the directory
    read only, but all have annoying caveats.

    In order of preference:

     1. You can change the scripts to all invoke "python -B" which prevents
        newer versions of python from writing out the .pyc files, side stepping
        the problem.

        Trouble is, some older versions of python don't support -B flag,
        and fail to run the script if -B is specified.

        But if your network has python versions that all support -B,
        this could be your best solution.

        One problem though is Windows: if a user clicks on a python script,
        it may run it without the -B flag, generating the pyc.
        You probably have to change the .py file association on all machines
        to make sure the -B flag is included. Good luck with that.

        This also means any time the script wants to invoke itself, it has
        to include the -B flag, e.g. os.system("python -B /path/to/script.py -arg..")

     2. You can set the PYTHONDONTWRITEBYTECODE environment variable;
        if set, python won't try to write out the .pyc file.

        But this is hard in practice.

        The variable must be set BEFORE the python script runs, as the .pyc
        is generated before the script runs. So you can't, for instance,
        try setting the variable in the script.

        And universally setting environment variables for the desktop environment
        can be hard on some platforms (Mac, Linux).

        All it takes is one guy to run the script without the variable set,
        and the .pyc file will be created and cause trouble for everyone again.

     3. Standardize on one version of python, and make it the default
        on all machines, avoiding compatibility issues.

        Trouble with that is you can break OS scripts that depend on specific
        versions of python. Python has become a popular enough language that
        operating systems now use it (e.g. in crontabs) to maintain the system.
        Breaking those scripts can cause the OS to misbehave in unexpected ways.

        And again, all it takes is one guy to accidentally use the wrong version
        of python from a random machine to create an incompatible .pyc.

    So unless python comes along with something better, I'm thinking
    removing write permission is on the script directory is the best
    approach.

    It'd be nice if one could put a ".nopyc" file in the same directory as
    the script(s), so if python sees that, it would skip generating/using .pyc files.
    Too late for that though for the current issue, as old versions of python
    don't have that feature.

Last Next