Skip to content

Commit

Permalink
Add process name customization (#212)
Browse files Browse the repository at this point in the history
  • Loading branch information
gi0baro authored Feb 13, 2024
1 parent ee475e0 commit 459a864
Show file tree
Hide file tree
Showing 7 changed files with 49 additions and 3 deletions.
1 change: 1 addition & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ lint-rust:
-A clippy::module-name-repetitions \
-A clippy::must-use-candidate \
-A clippy::needless-pass-by-value \
-A clippy::no-effect-underscore-binding \
-A clippy::similar-names \
-A clippy::single-match-else \
-A clippy::struct-excessive-bools \
Expand Down
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,9 @@ Options:
changes (requires granian[reload] extra)
[env var: GRANIAN_RELOAD; default:
(disabled)]
--process-name TEXT Set a custom name for processes (requires
granian[pname] extra) [env var:
GRANIAN_PROCESS_NAME]
--version Shows the version and exit
--install-completion [bash|zsh|fish|powershell|pwsh]
Install completion for the specified shell.
Expand Down
9 changes: 9 additions & 0 deletions granian/_imports.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
try:
import setproctitle
except ImportError:
setproctitle = None

try:
import watchfiles
except ImportError:
watchfiles = None
5 changes: 5 additions & 0 deletions granian/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,10 @@ def main(
help="Enable auto reload on application's files changes (requires granian[reload] extra)",
show_default='disabled',
),
process_name: Optional[str] = typer.Option(
None,
help='Set a custom name for processes (requires granian[pname] extra)',
),
_: Optional[bool] = typer.Option(
None,
'--version',
Expand Down Expand Up @@ -163,4 +167,5 @@ def main(
url_path_prefix=url_path_prefix,
respawn_failed_workers=respawn_failed_workers,
reload=reload,
process_name=process_name,
).serve()
26 changes: 23 additions & 3 deletions granian/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@

from ._futures import future_watcher_wrapper
from ._granian import ASGIWorker, RSGIWorker, WSGIWorker
from ._imports import setproctitle, watchfiles
from ._internal import load_target
from .asgi import LifespanProtocol, _callback_wrapper as _asgi_call_wrap
from .constants import HTTPModes, Interfaces, Loops, ThreadModes
Expand Down Expand Up @@ -87,6 +88,7 @@ def __init__(
url_path_prefix: Optional[str] = None,
respawn_failed_workers: bool = False,
reload: bool = False,
process_name: Optional[str] = None,
):
self.target = target
self.bind_addr = address
Expand All @@ -109,6 +111,7 @@ def __init__(
self.url_path_prefix = url_path_prefix
self.respawn_failed_workers = respawn_failed_workers
self.reload_on_changes = reload
self.process_name = process_name

configure_logging(self.log_level, self.log_config, self.log_enabled)

Expand Down Expand Up @@ -137,6 +140,7 @@ def build_ssl_context(self, cert: Optional[Path], key: Optional[Path]):
@staticmethod
def _spawn_asgi_worker(
worker_id,
process_name,
callback_loader,
socket,
loop_impl,
Expand All @@ -156,6 +160,8 @@ def _spawn_asgi_worker(
):
from granian._loops import loops, set_loop_signals

if process_name:
setproctitle.setproctitle(f'{process_name} worker-{worker_id}')
configure_logging(log_level, log_config, log_enabled)

loop = loops.get(loop_impl)
Expand Down Expand Up @@ -184,6 +190,7 @@ def _spawn_asgi_worker(
@staticmethod
def _spawn_rsgi_worker(
worker_id,
process_name,
callback_loader,
socket,
loop_impl,
Expand All @@ -203,6 +210,8 @@ def _spawn_rsgi_worker(
):
from granian._loops import loops, set_loop_signals

if process_name:
setproctitle.setproctitle(f'{process_name} worker-{worker_id}')
configure_logging(log_level, log_config, log_enabled)

loop = loops.get(loop_impl)
Expand Down Expand Up @@ -230,6 +239,7 @@ def _spawn_rsgi_worker(
@staticmethod
def _spawn_wsgi_worker(
worker_id,
process_name,
callback_loader,
socket,
loop_impl,
Expand All @@ -249,6 +259,8 @@ def _spawn_wsgi_worker(
):
from granian._loops import loops, set_loop_signals

if process_name:
setproctitle.setproctitle(f'{process_name} worker-{worker_id}')
configure_logging(log_level, log_config, log_enabled)

loop = loops.get(loop_impl)
Expand Down Expand Up @@ -280,6 +292,7 @@ def _spawn_proc(self, idx, target, callback_loader, socket_loader) -> Worker:
target=target,
args=(
idx + 1,
self.process_name,
callback_loader,
socket_loader(),
self.loop,
Expand Down Expand Up @@ -399,9 +412,7 @@ def _serve(self, spawn_target, target_loader):
self.shutdown()

def _serve_with_reloader(self, spawn_target, target_loader):
try:
import watchfiles
except ModuleNotFoundError:
if watchfiles is None:
logger.error('Using --reload requires the granian[reload] extra')
sys.exit(1)

Expand Down Expand Up @@ -451,5 +462,14 @@ def serve(
if self.http == HTTPModes.http2:
logger.info('Websockets are not supported on HTTP/2 only')

if setproctitle is not None:
self.process_name = self.process_name or (
f'granian {self.interface.value} {self.bind_addr}:{self.bind_port} {self.target}'
)
setproctitle.setproctitle(self.process_name)
elif self.process_name is not None:
logger.error('Setting process name requires the granian[pname] extra')
sys.exit(1)

serve_method = self._serve_with_reloader if self.reload_on_changes else self._serve
serve_method(spawn_target, target_loader)
5 changes: 5 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,9 @@ dependencies = [
]

[project.optional-dependencies]
pname = [
'setproctitle~=1.3.3',
]
reload = [
'watchfiles~=0.21',
]
Expand All @@ -50,6 +53,8 @@ test = [
'pytest-asyncio~=0.21.1',
'websockets~=11.0',
]
all = ['granian[pname,reload]']
dev = ['granian[all,lint,test]']

[project.urls]
Homepage = 'https://github.com/emmett-framework/granian'
Expand Down
3 changes: 3 additions & 0 deletions src/runtime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,7 @@ where
// This is generally ~55% faster than `pyo3_asyncio.future_into_py` implementation.
// It consumes more cpu-cycles than `future_into_py_futlike`,
// but for "quick" operations it's something like 12% faster.
#[allow(unused_must_use)]
pub(crate) fn future_into_py_iter<R, F, T>(rt: R, py: Python, fut: F) -> PyResult<&PyAny>
where
R: Runtime + ContextExt + Clone,
Expand All @@ -215,6 +216,7 @@ where
// This is generally ~38% faster than `pyo3_asyncio.future_into_py` implementation.
// It won't consume more cpu-cycles than standard asyncio implementation,
// and for "long" operations it's something like 6% faster than `future_into_py_iter`.
#[allow(unused_must_use)]
pub(crate) fn future_into_py_futlike<R, F, T>(rt: R, py: Python, fut: F) -> PyResult<&PyAny>
where
R: Runtime + ContextExt + Clone,
Expand Down Expand Up @@ -255,6 +257,7 @@ pub(crate) fn empty_future_into_py(py: Python) -> PyResult<&PyAny> {
Ok(PyEmptyAwaitable {}.into_py(py).into_ref(py))
}

#[allow(unused_must_use)]
pub(crate) fn run_until_complete<R, F, T>(rt: R, event_loop: &PyAny, fut: F) -> PyResult<T>
where
R: Runtime + ContextExt + Clone,
Expand Down

0 comments on commit 459a864

Please sign in to comment.