Hi there - sorry for the very long delay in between posts. Firstly: I had followed your instructions and got my multi-website setup to work in my testing environment. I have yet to try it in a production environment, but I am sure it should work there too. Thanks for the speedy and concise help you have given thus far.
I did, however, have a few additional questions surrounding the workings of Apache and mod_wsgi, at the time that I read and implemented your suggestion (i.e. in early February). This is largely because while getting results from following advice is fine, I wanted to be sure I really understood what I was working with, and why things (such as particular configuration snippets) work the way they do. I could have asked vague and probably malformed questions at that time, but decided instead to spend some time studying systems programming and looking over Apache/mod_wsgi documentation in order to ask better questions, hence the long time delay between now and my previous post. Now, regarding the distinction between "embedded mode" and "daemon mode", I take the main difference to consist in how the Python interpreter is executed from the perspective of the operating system. (I am aware that my setup is Windows and therefore supports only embedded mode - I am asking this more for understanding what I am working with rather than getting some specific thing to work.) In "embedded mode", the Apache worker process has the Python interpreter running "inside of it" - perhaps as its own thread. (In other words, whatever the Apache worker natively does is one thread, and the Python interpreter is another.) Whereas in "daemon mode", the worker process requests the OS to spin up a new process that is reserved for the Python interpreter to run in (which will then presumably execute WSGI applications). In the case of "embedded mode", I am having some trouble understanding what it means for a Python interpreter to run inside of a (pre-existing) process, such as an Apache worker. It may also be worth mentioning that the details of what exactly is involved with an Apache worker process and what it does is still opaque to me at the moment. The above confusion is related to my next question. In the mod_wsgi documentation (and in other places such as PEP 684), there are mentions of "sub-interpreters", which I am also having trouble conceptualising, even after reading resources about it online. What does it mean for a single process to host multiple sub-interpreters? (This is really what I want to understand for my own setup.) Are these sub-interpreters instantiated as threads within the process - i.e. each sub-interpreter is its own thread? If so, what within the process is coordinating these various threads (i.e. sub-interpreters), and what exactly is shared between these sub-interpreters? I am making the assumption of viewing this purely from the perspective of the operating system (and not from, say, the programmer working on a WSGI application). Hence I assume that the only thing that really exists are the processes that are defined by the OS. Consequently, how programs are threaded, and the logic of how threads are coordinated, is left to the logic of the various programs running inside of the process. To relate an infographic <https://realpython.com/cdn-cgi/image/width=706,format=auto/https://files.realpython.com/media/interpreter-storage.3b268b1beba9.png> on sub-interpreters that added to my confusion, here it shows what is presumably a single process used to run multiple Python sub-interpreters. The language suggests that each sub-interpreter can host multiple threads, but that each sub-interpreter is itself somehow not a thread (despite presumably belonging to the same process, which hosts the global state storage that all sub-interpreters share). Additionally, I am unclear on what the relation is between the main interpreter and subsequently-spawned sub-interpreters, along with how the process coordinates between these interpreters; I am assuming that, possibly, the main interpreter does the work of coordinating between which sub-interpreters run at which time (e.g. after exhausting their allotted quanta). The main point is that I am used to associating a Python interpreter with a process (e.g. seeing "python.exe" show up in the list of processes). I am neither familiar with the notion of what it means to run the interpreter inside of another pre-existing process (that's already running something else, like an Apache worker), nor what it means for a process to contain sub-interpreters. If any of my questions/concerns are unclear, I can try to rephrase them better. On Wednesday, February 12, 2025 at 2:34:07 PM UTC-6 Graham Dumpleton wrote: > WSGIPythonHome may indeed work on Windows now. It wasn't originally from > memory as the Python C APIs to support it on Windows were broken. I use a > hack to make it work now I think. > > Environment variables are a thing mainly used on UNIX systems rather than > on Windows. You don't have to use them to inject config into applications, > but I think that is the only way Django supports DJANGO_SETTINGS_MODULE. > > Anyway, try not using WSGIPythonPath. Instead, at the start of the WSGI > script file, what you reference using WSGIScriptAlias, set the Python > module path explicitly in Python code before anything else. > > Thus one script file would use: > > import sys, os > sys.path.insert(0, "C:/.../website_projects/example1.com") > os.environ["DJANGO_SETTINGS_MODULE"] = "main.settings" > > and the other: > > import sys, os > sys.path.insert(0, "C:/.../website_projects/example2.com > <http://example1.com>") > os.environ["DJANGO_SETTINGS_MODULE"] = "main.settings" > > Follow this will original contents of the file. > > This way the Python module path changes are specific to each Python sub > interpreter context and not setting it globally such that all inherit the > same. > > Graham > > On 13 Feb 2025, at 6:08 am, SC <[email protected]> wrote: > > Thanks for the article. I followed through on making that one change to my > wsgi.py configuration, and (after making two other changes) things are > working on my end. > > One of the other changes was to move WSGIPythonPath outside of the > virtual hosts, then use both paths within the same directive (separated by > a semicolon). > > However, there is one point of a lack of understanding on my part that's > bothering me. What exactly *is* an environment variable, and why is it > even needed? (Why not just read a value from a text file? - I understand > these questions are on the topic of Django moreso than WSGI, so if it's too > out of scope, then I'll bring it up over there rather than here.) I'm > asking because each one of my website projects has the following file > structure: > website-name-here1.com > | > ---- manage.py > | > ---- main > | > ---- app1 > | > ---- app2 > | > ... > > Normally, when running Django's "startproject" command as per their > tutorial, you get: > websitenamehere1com > | > ---- manage.py > | > ---- websitenamehere1com > | > ---- app1 > | > ---- app2 > | > ... > > Personally, I found the semantics of this default way of naming things > unacceptable (to my mind), so I changed it to the above. This is a > convention I would strongly prefer to keep. However, this ended up with the > very first Django instance running fine, and the second Django instance > throwing 400 "Bad Request" errors to the client. > More specifically, if I configured WSGIPythonPath to " > C:/.../website_projects/*example1.com <http://example1.com>* > ;C:/.../website_projects/*example2.com <http://example2.com/>*", it will > be example2.com that throws 400 errors. If I reverse the order of that > configuration, it will be example1.com that throws 400 errors. Both > website-projects had their folder named "main" (i.e. the same name), so > naturally, both of their wsgi.py files had: > os.environ['DJANGO_SETTINGS_MODULE'] = 'main.settings' > > While I had already suspected that this could cause problems (reusing the > same name, "main") when running multiple instances, I do not have a > detailed understanding of why. I thought that we would have one Apache > process spawning two Python processes (I should note that I have next to no > understanding of systems programming, Apache, WSGI, etc.), and each Python > process would have its own local set of environment variables and look only > within its own directory (as specified in WSGIPythonPath). It could > inexplicably be the case that setting the environment variable, " > DJANGO_SETTINGS_MODULE", is global, and therefore sets the value > "main.settings" for both Django instances, but even if this were the case, > it shouldn't matter. (I.e. they both use the same value for that variable - > unless it somehow gets resolved behind the scenes to an absolute filepath.) > So it might instead have to do with WSGIPythonPath being confused as to > which "main.settings" it should be picking from the two paths that I > configured for it. > > Either way, while I have a "fix", I would like to understand what is going > on here and whether the above convention can be kept with some workaround. > > Also, off-topic; the current docs state that the WSGIPythonHome directive is > not available on Windows > <https://www.modwsgi.org/en/latest/configuration-directives/WSGIPythonHome.html>, > > but it appears to be required to have around, or else Django will throw an > error. > On Tuesday, February 11, 2025 at 2:12:41 AM UTC-6 Graham Dumpleton wrote: > >> Have a read of: >> >> * http://blog.dscpl.com.au/2012/10/requests-running-in-wrong-django.html >> >> and see if it answers your questions. >> >> Most likely since using Django any problems will be due to how >> DJANGO_SETTINGS_MODULE is set. The default that project template generates >> causes problems and you need to change it. >> >> Graham >> >> On 11 Feb 2025, at 2:30 pm, SC <[email protected]> wrote: >> >> Firstly, I'd like to thank you for maintaining this excellent utility. >> >> I should note that I am not a technically-versed person in these matters, >> so apologies if I am slow to pick up on things. >> >> As for my problem, I want to host multiple Django-based websites from the >> same httpd.exe process on Windows. I do not know how to go about this. >> >> Details on my setup: >> - mod_wsgi (v5.0.0) >> - Python (v3.9.2) >> - Apache (v2.4.58), with the binaries being distributed by ApacheLounge >> (2024-01-31 release) >> - Windows Server 2012 R1 (and not R2) >> - Django v4.2.10 >> - I am hosting two distinct domain names (e.g. "someexample.com" and " >> anotherexample.com") >> - Everything is set up from scratch in the sense that I have physical >> access to the machine running the web server, access to the domain names, >> access to the router, etc. >> - Because I am running Windows, I am running mod_wsgi in embedded mode. >> My installation of Apache is mostly standard, so something like mod_python >> is not installed. >> >> Here is the relevant snippet of Apache configuration. I am aware that it >> does not run (because WSGIPythonPath cannot be in a VirtualHost directive), >> but it encapsulates the logic of what I am trying to go for: >> >> LoadFile >> "C:/Users/Administrator/AppData/Local/Programs/Python/Python39/python39.dll" >> LoadModule wsgi_module >> "C:/Users/Administrator/Desktop/SERVERS/web_server_production/python_virtual_environment/lib/site-packages/mod_wsgi/server/mod_wsgi.cp39-win_amd64.pyd" >> >> WSGIPythonHome >> "C:/Users/Administrator/Desktop/SERVERS/web_server_production/python_virtual_environment" >> >> >> # VIRTUAL HOST: "someexample.com" >> >> <VirtualHost *:80> >> ServerName someexample.com >> ServerAlias www.someexample.com >> Redirect permanent "/" "https://someexample.com/" >> </VirtualHost> >> >> <VirtualHost *:443> >> ServerName someexample.com >> ServerAlias someexample.com >> <If "%{HTTP_HOST} == 'www.someexample.com'"> >> Redirect "/" "https://someexample.com/" >> </If> >> >> SSLEngine On >> SSLCertificateFile "..." >> SSLCertificateKeyFile "..." >> >> WSGIScriptAlias / >> "C:/Users/Administrator/Desktop/SERVERS/web_server_production/website_projects/ >> someexample.com/main/wsgi.py" >> WSGIPythonPath >> "C:/Users/Administrator/Desktop/SERVERS/web_server_production/website_projects/ >> someexample.com" >> >> <Directory >> "C:/Users/Administrator/Desktop/SERVERS/web_server_production/website_projects/ >> someexample.com/main"> >> <Files "wsgi.py"> >> Require all granted >> </Files> >> </Directory> >> >> Alias "/assets/" >> "C:/Users/Administrator/Desktop/SERVERS/web_server_production/website_projects/ >> someexample.com/@assets/" >> >> <Directory >> "C:/Users/Administrator/Desktop/SERVERS/web_server_production/website_projects/ >> someexample.com/@assets/"> >> Require all granted >> </Directory> >> >> </VirtualHost> >> >> >> # VIRTUAL HOST: "anotherexample.com" >> >> <VirtualHost *:80> >> ServerName anotherexample.com >> ServerAlias www.anotherexample.com >> Redirect permanent "/" "https://anotherexample.com/" >> </VirtualHost> >> >> <VirtualHost *:443> >> ServerName anotherexample.com >> ServerAlias www.anotherexample.com >> <If "%{HTTP_HOST} == 'www.anotherexample.com'"> >> Redirect "/" "https://anotherexample.com/" >> </If> >> >> SSLEngine On >> SSLCertificateFile "..." >> SSLCertificateKeyFile "..." >> >> WSGIScriptAlias / >> "C:/Users/Administrator/Desktop/SERVERS/web_server_production/website_projects/ >> anotherexample.com/main/wsgi.py" >> WSGIPythonPath >> "C:/Users/Administrator/Desktop/SERVERS/web_server_production/website_projects/ >> anotherexample.com" >> >> <Directory >> "C:/Users/Administrator/Desktop/SERVERS/web_server_production/website_projects/ >> anotherexample.com/main"> >> <Files "wsgi.py"> >> Require all granted >> </Files> >> </Directory> >> >> Alias "/assets/" >> "C:/Users/Administrator/Desktop/SERVERS/web_server_production/website_projects/ >> anotherexample.com/@assets/" >> >> <Directory >> "C:/Users/Administrator/Desktop/SERVERS/web_server_production/website_projects/ >> anotherexample.com/@assets/"> >> Require all granted >> </Directory> >> >> </VirtualHost> >> >> >> If further information is needed, such as what my file structure for >> these website (Django) projects look like, or how wsgi.py is configured (I >> *mostly* used the default generation given by Django), I can provide >> those too. >> >> Probably worth noting, but when starting a new project with Django, it >> usually names the "main package" as whatever the project was named (usually >> the name is just the website's name). Instead, I renamed that package to >> "main" (and it has the same name across all websites) and updated the >> appropriate references (e.g. "main.settings" inside of "wsgi.py" instead of >> "someexamplecom.settings"). This is a convention I would prefer to keep, if >> possible, though I am willing to revert to Django's default convention if >> it is truly necessary. >> >> >> -- >> You received this message because you are subscribed to the Google Groups >> "modwsgi" group. >> To unsubscribe from this group and stop receiving emails from it, send an >> email to [email protected]. >> To view this discussion visit >> https://groups.google.com/d/msgid/modwsgi/d2b81c58-9d22-4720-a6d3-38eec28ca9a8n%40googlegroups.com >> >> <https://groups.google.com/d/msgid/modwsgi/d2b81c58-9d22-4720-a6d3-38eec28ca9a8n%40googlegroups.com?utm_medium=email&utm_source=footer> >> . >> >> >> > -- > You received this message because you are subscribed to the Google Groups > "modwsgi" group. > To unsubscribe from this group and stop receiving emails from it, send an > email to [email protected]. > > To view this discussion visit > https://groups.google.com/d/msgid/modwsgi/39ecdbd5-5e04-4269-96df-86a998936e11n%40googlegroups.com > > <https://groups.google.com/d/msgid/modwsgi/39ecdbd5-5e04-4269-96df-86a998936e11n%40googlegroups.com?utm_medium=email&utm_source=footer> > . > > > -- You received this message because you are subscribed to the Google Groups "modwsgi" group. To unsubscribe from this group and stop receiving emails from it, send an email to [email protected]. To view this discussion visit https://groups.google.com/d/msgid/modwsgi/3ac445cc-8fe8-4dd6-8321-5ed2fc0e1266n%40googlegroups.com.
