Skip to content

Fix Docker Build Errors and Refactor the code in src/flask.py #189

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 8 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@ build/
*.db
*.egg-info/
*.pyc
foo.db
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
FROM cs50/cli

RUN sudo apt update && sudo apt install --yes libmysqlclient-dev pgloader pkg-config postgresql
RUN sudo apt update && sudo apt install --yes libmysqlclient-dev pgloader pkg-config postgresql && sudo rm -rf /var/lib/apt/lists/*
RUN sudo pip3 install mysqlclient psycopg2-binary

WORKDIR /mnt
4 changes: 2 additions & 2 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ services:
container_name: python-cs50
depends_on:
- mysql
- postgres
- postgres
environment:
MYSQL_HOST: mysql
POSTGRESQL_HOST: postgres
Expand All @@ -30,4 +30,4 @@ services:
POSTGRES_PASSWORD: postgres
POSTGRES_DB: test
ports:
- 5432:5432
- 5432:5432
79 changes: 62 additions & 17 deletions src/cs50/flask.py
Original file line number Diff line number Diff line change
@@ -1,47 +1,92 @@
import importlib.util
import os
import pkgutil
import sys
import warnings


def _wrap_flask(f):
"""
Wraps the Flask class's __init__ method if necessary,
primarily to add ProxyFix for CS50 IDE online environments.
"""
if f is None:
return

from packaging.version import Version, InvalidVersion
from .cs50 import _formatException
# Dynamically import packaging only when needed
try:
from packaging.version import Version, InvalidVersion
except ImportError:
# If packaging is not installed, cannot check version, so assume we can't wrap
return


try:
# Check Flask version compatibility
if Version(f.__version__) < Version("1.0"):
return
except InvalidVersion:
except (InvalidVersion, AttributeError):
# Handle invalid version strings or if Flask doesn't have __version__
return
except Exception:
return

# Apply ProxyFix only in CS50 IDE online environment
if os.getenv("CS50_IDE_TYPE") == "online":
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@rongxin-liu do we need this for cs50.dev?

from werkzeug.middleware.proxy_fix import ProxyFix
try:
from werkzeug.middleware.proxy_fix import ProxyFix
except ImportError:
# If werkzeug (a Flask dependency) is missing or ProxyFix moved, cannot apply fix
return

_flask_init_before = f.Flask.__init__

def _flask_init_after(self, *args, **kwargs):
_flask_init_before(self, *args, **kwargs)
self.wsgi_app = ProxyFix(
self.wsgi_app, x_proto=1
) # For HTTPS-to-HTTP proxy
# Apply ProxyFix to handle reverse proxies in CS50 IDE (x_proto=1 trusts X-Forwarded-Proto header)
self.wsgi_app = ProxyFix(self.wsgi_app, x_proto=1)

# Monkey-patch Flask's __init__
f.Flask.__init__ = _flask_init_after


# If Flask was imported before cs50
# --- Main Logic ---

# Check if Flask was already imported before cs50 library was imported
if "flask" in sys.modules:
_wrap_flask(sys.modules["flask"])

# If Flask wasn't imported
# If Flask hasn't been imported yet, set up patching for when it *is* imported
else:
flask_loader = pkgutil.get_loader("flask")
if flask_loader:
_exec_module_before = flask_loader.exec_module
# Find the module specification for Flask using the recommended importlib utility
# This replaces the deprecated pkgutil.get_loader
flask_spec = importlib.util.find_spec("flask")

# Check if the spec was found and if it has a loader
# (Some namespace packages might not have a loader, but Flask should)
if flask_spec and flask_spec.loader:

# Ensure the loader has the exec_module method (standard loaders do)
if hasattr(flask_spec.loader, 'exec_module'):

# Get the original exec_module method from the loader
_exec_module_before = flask_spec.loader.exec_module

# Define a wrapper function for exec_module
# This function will be called by the import system when Flask is loaded
def _exec_module_after(module):
# Execute the original module loading logic first
_exec_module_before(module)
# Now that the module ('flask') is fully loaded and present in sys.modules,
# apply our custom wrapping logic.
_wrap_flask(module) # Pass the loaded module object to _wrap_flask

# Monkey-patch the loader's exec_module method with our wrapper
flask_spec.loader.exec_module = _exec_module_after
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@rongxin-liu mind reviewing?


def _exec_module_after(*args, **kwargs):
_exec_module_before(*args, **kwargs)
_wrap_flask(sys.modules["flask"])
else:
# Handle the unlikely case where the loader doesn't have exec_module
warnings.warn("Flask loader doesn't support the expected exec_module interface")

flask_loader.exec_module = _exec_module_after
else:
# Handle the case where Flask spec wasn't found (Flask not installed?)
warnings.warn("Flask module specification not found. Flask may not be installed.")