Skip to main content

httpout allows you to execute your Python script from a web URL, the print() output goes to your browser.

Project description

httpout

httpout allows you to execute your Python script from a web URL, the print() output goes to your browser.

This is the classic way to deploy your scripts to the web. You just need to put your regular .py files as well as other static files in the document root and each will be routable from the web. No server reload is required!

It's similar to CGI, except that httpout doesn't spawn a new process for each request, making it more lightweight in terms of resource usage. The disadvantage is that it's not isolated; your script is executed dynamically by the server using exec() within the same process.

It provides a native experience for running your script from the web, and the remaining drawbacks can be mitigated in this era using containerization. The tip is: don't run Python as root.

How does it work?

httpout will assign every route either like /hello.py or /index.py with the name __main__ and executes it as a module in a thread pool. Monkey patching is done at the module-level by hijacking the __import__.

In the submodules perspective, the __main__ object points to the main module such as /hello.py, rather than pointing to sys.modules['__main__'] or the web server itself.

httpout does not perform a cache mechanism like standard imports or with sys.modules to avoid conflicts with other modules / requests. Because each request must have its own namespace.

To keep it simple, only the main module is cached (as code object). The cache will be valid during HTTP Keep-Alive. So if you just change the script there is no need to reload the server process, just wait until the connection is lost.

Keep in mind this may not work for running complex python scripts, e.g. running other server processes or multithreaded applications as each route is not a real main thread.

httpout

Install

python3 -m pip install --upgrade httpout

Example

# hello.py
import time


print('<pre>Hello...')

time.sleep(1)
print('and')

time.sleep(2)
print('Bye!</pre>')

Put hello.py in the example/ folder, then run the httpout server with:

python3 -m httpout --port 8000 example/

and your hello.py can be accessed at http://localhost:8000/hello.py. If you don't want the .py suffix in the URL, you can instead create a hello/ folder with index.py inside.

Handling forms

This is an overview of how to view request methods and read form data.

# form.py
import sys

from httpout import request, response


method_str = __server__.REQUEST_METHOD
method_bytes = request.method


if method_str != 'POST':
    response.set_status(405, 'Method Not Allowed')
    print('Method Not Allowed')
    sys.exit()


form_data = wait(request.form())

print(method_str, method_bytes, form_data)

It can also be written this way:

# form.py
import sys

from httpout import request, response


method_str = __server__.REQUEST_METHOD
method_bytes = request.method


if method_str != 'POST':
    response.set_status(405, 'Method Not Allowed')
    print('Method Not Allowed')
    sys.exit()


async def main():
    form_data = await request.form()

    print(method_str, method_bytes, form_data)


wait(main())

Then you can do:

curl -d foo=bar http://localhost:8000/form.py

Features

httpout is designed to be fun. It's not built for perfectionists. httpout has:

  • A hybrid async and sync, the two worlds can coexist in your script seamlessly; It's not yet time to drop your favorite synchronous library
  • More lightweight than running CGI scripts
  • Your print()s are sent immediately line by line without waiting for the script to finish like a typical CGI
  • No need for a templating engine, just do if-else and print() making your script portable for both CLI and web
  • And more

Hybrid async and sync

httpout server runs each module in a separate thread asynchronously. You can run blocking codes like time.sleep() without bothering server's loop. You can also run coroutine functions at once with wait(). Although regular asyncio.run() will do. The difference is that the former uses an existing event loop rather than creating a new one every time.

# ...
time.sleep(1)

async def main():
   await asyncio.sleep(1)

wait(main())
print('Done!')

For your information, wait() is a shorthand of:

fut = run(main())
fut.result()

where fut is a concurrent.futures.Future object.

Builtin objects

No need to import anything to access these, except __main__ which can be imported to honor the semantics.

  • print()
  • run(), runs a coroutine without waiting, it returns a concurrent.futures.Future object
  • wait() , runs a coroutine and wait until done, it returns the result
  • __main__, a reference to your main route, available across your submodule imports
  • __server__, a dict object containing basic HTTP request information and etc.
  • __globals__, a worker/app-level context. to initialize objects at worker start, you can place them in __globals__.py

Security

It's important to note that httpout only focuses on request security; to ensure that path traversal through the URL never happens.

httpout will never validate the script you write, you can still access objects like os, eval(), open(), even traversal out of the document root. So this stage is your responsibility.

FYI, PHP used to have something called Safe Mode, but it was deemed architecturally incorrect, so they removed it.

The PHP safe mode is an attempt to solve the shared-server security problem. It is architecturally incorrect to try to solve this problem at the PHP level, but since the alternatives at the web server and OS levels aren't very realistic, many people, especially ISP's, use safe mode for now.

License

MIT License

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

httpout-0.0.16.tar.gz (13.7 kB view details)

Uploaded Source

Built Distribution

httpout-0.0.16-py3-none-any.whl (12.5 kB view details)

Uploaded Python 3

File details

Details for the file httpout-0.0.16.tar.gz.

File metadata

  • Download URL: httpout-0.0.16.tar.gz
  • Upload date:
  • Size: 13.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/4.0.2 CPython/3.7.9

File hashes

Hashes for httpout-0.0.16.tar.gz
Algorithm Hash digest
SHA256 dc4a64455e6760dfb98d9987fdf646e10f8058f89abd3c3977a3db26b6dbae40
MD5 5f5919ae11f4d19d2ff88437db71925d
BLAKE2b-256 eae075c88de55b29f698a50a241d9c9a72786f2b7d299520eb33aaaaa1149bac

See more details on using hashes here.

File details

Details for the file httpout-0.0.16-py3-none-any.whl.

File metadata

  • Download URL: httpout-0.0.16-py3-none-any.whl
  • Upload date:
  • Size: 12.5 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/4.0.2 CPython/3.7.9

File hashes

Hashes for httpout-0.0.16-py3-none-any.whl
Algorithm Hash digest
SHA256 f7279215a341ac3b0aa73e64e078b1187cacc1bf3a70e19595d2a32d3d9feb92
MD5 8915a329cdeaf3c1c8dbbf3e7065d8b3
BLAKE2b-256 aa56be224162f21203430cad67912624ae9de7cd87a730fc0ce2cbca0d2e6cdb

See more details on using hashes here.

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page