-
-
Notifications
You must be signed in to change notification settings - Fork 219
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
CLI: Load from file or module. Add software tests and documentation.
- Loading branch information
Showing
14 changed files
with
652 additions
and
23 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -7,6 +7,7 @@ | |
.pytest_cache | ||
.DS_Store | ||
coverage.xml | ||
.coverage* | ||
|
||
__pycache__ | ||
tests/__pycache__ | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,90 @@ | ||
Responder CLI | ||
============= | ||
|
||
Responder installs a command line program ``responder``. Use it to launch | ||
a Responder application from a file or module. | ||
|
||
Launch application from file | ||
---------------------------- | ||
|
||
Acquire minimal example application, `helloworld.py`_, | ||
implementing a basic echo handler, and launch the HTTP service. | ||
|
||
.. code-block:: shell | ||
wget https://github.com/kennethreitz/responder/raw/refs/heads/main/examples/helloworld.py | ||
responder run helloworld.py | ||
In another terminal, invoke a HTTP request, for example using `HTTPie`_. | ||
|
||
.. code-block:: shell | ||
http http://127.0.0.1:5042/hello | ||
The response is no surprise. | ||
|
||
:: | ||
|
||
HTTP/1.1 200 OK | ||
content-length: 13 | ||
content-type: text/plain | ||
date: Sat, 26 Oct 2024 13:16:55 GMT | ||
encoding: utf-8 | ||
server: uvicorn | ||
|
||
hello, world! | ||
|
||
|
||
Launch application from module | ||
------------------------------ | ||
|
||
If your Responder application has been implemented as a Python module, | ||
launch it like this: | ||
|
||
.. code-block:: shell | ||
responder run acme.app | ||
That assumes a Python package ``acme`` including an ``app`` module | ||
``acme/app.py`` that includes an attribute ``api`` that refers | ||
to a ``responder.API`` instance, reflecting the typical layout of | ||
a standard Responder application. | ||
|
||
.. rubric:: Non-standard instance name | ||
|
||
When your attribute that references the ``responder.API`` instance | ||
is called differently than ``api``, append it to the launch target | ||
address like this: | ||
|
||
.. code-block:: shell | ||
responder run acme.app:service | ||
Within your ``app.py``, the instance would have been defined like this: | ||
|
||
.. code-block:: python | ||
service = responder.API() | ||
Build JavaScript application | ||
---------------------------- | ||
|
||
The ``build`` subcommand invokes ``npm run build``, optionally accepting | ||
a target directory. By default, it uses the current working directory, | ||
where it expects a regular NPM ``package.json`` file. | ||
|
||
.. code-block:: shell | ||
responder build | ||
When specifying a target directory, responder will change to that | ||
directory beforehand. | ||
|
||
.. code-block:: shell | ||
responder build /path/to/project | ||
.. _helloworld.py: https://github.com/kennethreitz/responder/blob/main/examples/helloworld.py | ||
.. _HTTPie: https://httpie.io/docs/cli |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -96,6 +96,7 @@ User Guides | |
deployment | ||
testing | ||
api | ||
cli | ||
|
||
|
||
Installing Responder | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,46 +1,78 @@ | ||
"""Responder. | ||
""" | ||
Responder CLI. | ||
A web framework for Python. | ||
Commands: | ||
run Start the application server | ||
build Build frontend assets using npm | ||
Usage: | ||
responder | ||
responder run [--build] <module> | ||
responder build | ||
responder run [--debug] [--limit-max-requests=] <target> | ||
responder build [<target>] | ||
responder --version | ||
Options: | ||
-h --help Show this screen. | ||
-v --version Show version. | ||
--debug Enable debug mode. | ||
--limit-max-requests=<n> Maximum number of requests to handle before shutting down. | ||
Arguments: | ||
<target> For run: Python module path (e.g., "app:api" loads api from app.py) | ||
For build: Directory containing package.json (default: current directory) | ||
Examples: | ||
responder run app:api # Run the API from app.py | ||
responder build # Build frontend assets | ||
""" | ||
|
||
import logging | ||
import os | ||
import subprocess | ||
import typing as t | ||
|
||
import docopt | ||
|
||
from .__version__ import __version__ | ||
from .util.python import load_target | ||
|
||
logger = logging.getLogger(__name__) | ||
|
||
def cli(): | ||
|
||
def cli() -> None: | ||
""" | ||
CLI interface handler of the Responder package. | ||
Main entry point for the Responder CLI. | ||
Parses command line arguments and executes the appropriate command. | ||
Supports running the application, building assets, and displaying version info. | ||
""" | ||
args = docopt.docopt(__doc__, argv=None, version=__version__, options_first=False) | ||
|
||
module = args["<module>"] | ||
build = args["build"] or args["--build"] | ||
run = args["run"] | ||
target: t.Optional[str] = args["<target>"] | ||
build: bool = args["build"] | ||
debug: bool = args["--debug"] | ||
run: bool = args["run"] | ||
|
||
if build: | ||
# S603, S607 are suppressed as we're using fixed arguments, not user input | ||
subprocess.check_call(["npm", "run", "build"]) # noqa: S603, S607 | ||
# FIXME: Windows can't find `npm`, at least not on GHA. | ||
# Using `npm.exe` also doesn't work? | ||
subprocess.check_call(["npm", "run", "build"], cwd=target, env=dict(os.environ)) # noqa: S603, S607 | ||
|
||
if run: | ||
split_module = module.split(":") | ||
|
||
if len(split_module) > 1: | ||
module = split_module[0] | ||
prop = split_module[1] | ||
else: | ||
prop = "api" | ||
# Maximum request limit. Terminating afterward. Suitable for software testing. | ||
limit_max_requests = args["--limit-max-requests"] | ||
if limit_max_requests is not None: | ||
try: | ||
limit_max_requests = int(limit_max_requests) | ||
if limit_max_requests <= 0: | ||
logger.error("limit-max-requests must be a positive integer") | ||
exit(1) | ||
except ValueError: | ||
logger.error("limit-max-requests must be a valid integer") | ||
exit(1) | ||
|
||
app = __import__(module) | ||
getattr(app, prop).run() | ||
# Launch Responder API server (uvicorn). | ||
api = load_target(target=target) | ||
api.run(debug=debug, limit_max_requests=limit_max_requests) |
Empty file.
Oops, something went wrong.