Table of Contents


I’m currently working on a Flask project and I wanted to combine two packages (Flask-CDN and Flask-Static-Digest) that both modify Flask’s url_for function.


However, these two packages are not made to work together. Flask-CDN overrides the url_for function in the context of templates. Once that function is called, it applies its processing and then calls Flask’s underlying url_for function. Flask-Static-Digest works similarly, but instead defines a new function static_url_for. This function still ends up calling url_for.

How everything originally works.
How everything originally works.

What I wanted was to have Flask-Static-Digest call the url_for function of Flask-CDN so that I could combine the features of both.

How I want it to work.
How I want it to work.

I really wanted to avoid editing code of the dependencies if at all possible, so I tried to see if I could get Flask-Static-Digest to import Flask-CDN while making it think it was importing Flask.


The answer is yes, you can do this, by overwriting sys.modules. Here is an example:

1def func(val):
2    print("A")
3    return val + 5

1def func(val):
2    print("B")
3    return val + 10

1import a
3def func(val):
4    return a.func(val)

 1import sys
 3import c
 5VAL = 20
 7print("The real value of c.func is: {}".format(c.func(VAL)))
 9# ideally, you would have never imported this module
11# create reference to real import so we don't lose it
12a_real_import = __import__("a")
13a_fake_import = __import__("b")
15# fake the import
16sys.modules["a"] = a_fake_import
17import c
18# set it back to the real value
19sys.modules["a"] = a_real_import
21print("The fake value of c.func is: {}".format(c.func(VAL)))

And after running

1$ > python
3The real value of c.func is: 25
5The fake value of c.func is: 30

The dictionary sys.module contains references to every module you have imported.

1Python 3.8.2 (tags/v3.8.2:7b3ab59, Feb 25 2020, 23:03:10) [MSC v.1916 64 bit (AMD64)] on win32
2Type "help", "copyright", "credits" or "license" for more information.
3>>> import sys
4>>> import string
5>>> sys.modules["string"]
6<module 'string' from 'C:\\Python38\\lib\\'>

All you have to do is inject or overwrite your own values. For reference, here is what I’ve done in my Flask app:

 1# static digest
 2# we need this to call the CDN's url_for and not flask
 3# don't try this at home, kids
 4flask_cdn_import = __import__("flask_cdn")
 5flask_real_import = __import__("flask")
 7# replace real flask import with fake flask cdn import
 8sys.modules["flask"] = flask_cdn_import
 9# import the flask digest module with the fake import
10from flask_static_digest import FlaskStaticDigest  # noqa
12# put the flask module back to what it was
13sys.modules["flask"] = flask_real_import
14# run the class init from flask digest
15static_digest = FlaskStaticDigest()


While this trick can be very useful, it can also very dangerous and easy to break stuff. Remember, with great power comes great responsibility.