Title: NoneType Object has no attribute 'write' for Flask app with Pyinstaller
Tags: javascript,python,pandas,flask,pyinstaller

Question: I wrote a multi-template Flask app and am trying to convert it to an executable with PyInstaller. The app has a form page that allows you to submit to a csv, and then it consumes that csv and converts it into a Pandas dataframe, which it displays. I have a button next to each rendered dataframe which 'downloads' the view, by dropping that particular dataframe to a csv using the build in pandas functions. Everything seems to work fine with the exception that when the app loads, I get the following error:
Traceback (most recent call last):
  File "app.py", line 426, in <module>
  File "flask\app.py", line 915, in run
  File "flask\cli.py", line 680, in show_server_banner
  File "click\utils.py", line 298, in echo
AttributeError: 'NoneType' object has no attribute 'write'


And the app crashes when I push any of these 'download' buttons.
Here is the relevant code:
app.py
from flask import Flask, abort, render_template, request, url_for, jsonify
from markupsafe import escape
import os
import datetime
from datetime import timedelta
import calendar
import csv
from csv import DictWriter
import pathlib
import pandas as pd
import json
import sys
import webbrowser
from threading import Timer

#This is how I can store my controller in the same directory as my master log file
sys.path.insert(1, "C:/Users/sam/webdev/crosstraining")
import PYcontroller

pd.set_option('display.max_columns', 20)

if getattr(sys, 'frozen', False):
    template_folder = os.path.join(sys._MEIPASS, "templates")
    static_folder = os.path.join(sys._MEIPASS, "static")
    app = Flask(__name__, template_folder=template_folder, static_folder=static_folder)
else:
    app = Flask(__name__)


app.config['TEMPLATES_AUTO_RELOAD'] = True

computers = ['home', 'laptop', 'work', 'production']
location = computers[0]

@app.route('/timekeeper/', methods=["GET","POST"])
def timekeeper():
    timekeeperdata = {
        'utc_dt':date_time_str,
        'day':today,
        'month':month,
        'file':table,
        'engineer':engineer,
        'jobs':jobs,
        'dates':dates
        }
    monthCategory = aggDF(master_crossTrainingLog, getMonth())
    yearCategory = aggDF(master_crossTrainingLog, getYear())
    crosstraining = crossTrainingDF(master_crossTrainingLog, engineer)
    admincrosstraining = admincrossTrainingDF(master_crossTrainingLog)
    #These lists are to make the download work
    all_dfs = [admincrosstraining, crosstraining, monthCategory, yearCategory, full_hours]
    all_df_names = ["Admin_view_crosstraining", "_crossTraining", "ThisMonthCrossTraining", "ThisYearCrossTraining", "MasterCrossTrainingFile"]
    #the df's in all_df's are ordered as they are used in timekeeper.
    if request.method == "POST":
        view = request.form.get("view")
        download_view(view, all_dfs, all_df_names)


    return render_template("timekeeper.html", timekeeperdata=timekeeperdata,
    #these are all of the pandas tables that have been made elsewhere 
    tables=[admincrosstraining.to_html(classes='data'),
            crosstraining.to_html(classes='data'),
            monthCategory.to_html(classes='data'),
            yearCategory.to_html(classes='data'),
            full_hours.to_html(classes='data')],

     titles=[admincrosstraining.columns.values,
        crosstraining.columns.values,
        monthCategory.columns.values,
        yearCategory.columns.values,
        full_hours.columns.values])

def download_view(viewNum, data, dataNames):
    df = data[int(viewNum)]
    if viewNum == 1:
        filename = str(engineer[1][3]) + str(dataNames[int(viewNum)])
    else:
        filename = str(dataNames[int(viewNum)])
    filepath = str(pathlib.Path.home() / "Downloads") +"\\"+ filename + "_"+date_time_str[:10] + ".csv"
    df.to_csv(filepath)
    print("file downloaded to: "+ filepath)

def open_browser():
    webbrowser.open_new('http://127.0.0.1:5000/')



if __name__=='__main__':
    Timer(1, open_browser).start()
    app.run() #<--- This is line 426


timekeeper template
  {% if timekeeperdata['engineer'][1][1] == 'admin' %}
    <h3> Team Hours to Date</h3>
    <div id="pandas" name="View0"> {{tables[0]|safe }} </div> <!--This is the Team hours-->

    <form action="{{ url_for('timekeeper')}}" method="post">
        <input type="text" name="view" value=0 style="display:none;">
        <button type="submit" class="btn" name='download' value="View0" >Download</button>       <p id="submitMessage"> Download Complete!</p>
    </form>

When you click the download button, it thinks for a minute and then returns a 500 Internal server error message. The Console in the dev tool says:
Status
500
INTERNAL SERVER ERROR
VersionHTTP/1.0
Transferred464 B (290 B size)
Referrer Policystrict-origin-when-cross-origin
Request PriorityHighest

I dont know what headers I would need to send in this instance to remove the CORS issue, if that's actually what's causing the error.
I'm just assuming the error on start and the way it throws an error when you try and 'download' a dataframe are related, but I'm not sure how. If they're unrelated, I'd love any advice on how to explore either. I'm not particularly versed in Pyinstaller or Flask.
There's more code but I'm pretty sure this is all that's relevant. If you think something is missing from a troubleshooting perspective, please feel free to let me know.
***EDIT: The app seems to work when I omit the -w flag from pyinstaller.

Answer: Im not sure, but I think is something related with Python 3.10, because its working when I use Python 3.9