From f28c5cb0427fb903bc653520adfe4839541c9c71 Mon Sep 17 00:00:00 2001 From: Clovis Kyndt Date: Thu, 7 Mar 2024 22:52:52 +0100 Subject: [PATCH] style(documentation): switch to mkdocs from sphinx --- Makefile | 40 +--- docs/creating-service.md | 102 ++++++++++ docs/index.md | 270 ++++++++------------------ docs/references/mode.loop.eventlet.md | 2 +- docs/references/mode.loop.gevent.md | 2 +- docs/references/mode.loop.uvloop.md | 2 +- docs/references/mode.md | 3 - mkdocs.yml | 36 ++-- mode/debug.py | 7 +- mode/locals.py | 60 +++--- mode/loop/__init__.py | 16 +- mode/loop/_gevent_loop.py | 2 +- mode/loop/eventlet.py | 2 +- mode/loop/gevent.py | 2 +- mode/loop/uvloop.py | 2 +- mode/services.py | 12 +- mode/supervisors.py | 2 +- mode/threads.py | 5 +- mode/timers.py | 6 +- mode/types/services.py | 4 +- mode/types/signals.py | 2 +- mode/types/supervisors.py | 2 +- mode/utils/aiter.py | 2 +- mode/utils/collections.py | 22 ++- mode/utils/compat.py | 2 +- mode/utils/futures.py | 4 +- mode/utils/imports.py | 29 ++- mode/utils/locals.py | 2 +- mode/utils/locks.py | 2 +- mode/utils/logging.py | 14 +- mode/utils/mocks.py | 2 +- mode/utils/objects.py | 10 +- mode/utils/queues.py | 10 +- mode/utils/text.py | 24 +-- mode/utils/times.py | 6 +- mode/utils/tracebacks.py | 4 +- mode/utils/trees.py | 2 +- mode/utils/types/graphs.py | 2 +- mode/utils/types/trees.py | 2 +- requirements-docs.txt | 11 +- 40 files changed, 359 insertions(+), 372 deletions(-) create mode 100644 docs/creating-service.md delete mode 100644 docs/references/mode.md diff --git a/Makefile b/Makefile index 15309713..58801857 100644 --- a/Makefile +++ b/Makefile @@ -8,18 +8,14 @@ TOX ?= tox NOSETESTS ?= nosetests ICONV ?= iconv MYPY ?= mypy -SPHINX2RST ?= sphinx2rst TESTDIR ?= t -SPHINX_DIR ?= docs/ -SPHINX_BUILDDIR ?= "${SPHINX_DIR}/_build" README ?= README.rst README_SRC ?= "docs/templates/readme.txt" CONTRIBUTING ?= CONTRIBUTING.rst CONTRIBUTING_SRC ?= "docs/contributing.rst" COC ?= CODE_OF_CONDUCT.rst COC_SRC ?= "docs/includes/code-of-conduct.txt" -SPHINX_HTMLDIR="${SPHINX_BUILDDIR}/html" DOCUMENTATION=Documentation all: help @@ -55,19 +51,15 @@ release: . PHONY: deps-default deps-default: - $(PIP) install -U -r requirements/default.txt - -. PHONY: deps-dist -deps-dist: - $(PIP) install -U -r requirements/dist.txt + $(PIP) install -U -e "." . PHONY: deps-docs deps-docs: - $(PIP) install -U -r requirements/docs.txt + $(PIP) install -U -r requirements-docs.txt . PHONY: deps-test deps-test: - $(PIP) install -U -r requirements/test.txt + $(PIP) install -U -r requirements-test.txt . PHONY: deps-extras deps-extras: @@ -80,12 +72,15 @@ develop: deps-default deps-dist deps-docs deps-test deps-extras . PHONY: Documentation Documentation: - (cd "$(SPHINX_DIR)"; $(MAKE) html) - mv "$(SPHINX_HTMLDIR)" $(DOCUMENTATION) + mkdocs build . PHONY: docs docs: Documentation +. PHONE: serve-docs +serve-docs: + mkdocs serve + clean-docs: -rm -rf "$(SPHINX_BUILDDIR)" @@ -94,34 +89,22 @@ ruff: lint: ruff apicheck readmecheck -apicheck: - (cd "$(SPHINX_DIR)"; $(MAKE) apicheck) - clean-readme: -rm -f $(README) readmecheck: $(ICONV) -f ascii -t ascii $(README) >/dev/null -$(README): - $(SPHINX2RST) "$(README_SRC)" --ascii > $@ - readme: clean-readme $(README) readmecheck clean-contrib: -rm -f "$(CONTRIBUTING)" -$(CONTRIBUTING): - $(SPHINX2RST) "$(CONTRIBUTING_SRC)" > $@ - contrib: clean-contrib $(CONTRIBUTING) clean-coc: -rm -f "$(COC)" -$(COC): - $(SPHINX2RST) "$(COC_SRC)" > $@ - coc: clean-coc $(COC) clean-pyc: @@ -161,9 +144,4 @@ typecheck: .PHONY: requirements requirements: $(PIP) install --upgrade pip;\ - for f in `ls requirements/` ; do $(PIP) install -r requirements/$$f ; done - -.PHONY: clean-requirements -clean-requirements: - pip freeze | xargs pip uninstall -y - $(MAKE) requirements + $(PIP) install -r requirements.txt diff --git a/docs/creating-service.md b/docs/creating-service.md new file mode 100644 index 00000000..1991f178 --- /dev/null +++ b/docs/creating-service.md @@ -0,0 +1,102 @@ + +# Creating a Service + +To define a service, simply subclass and fill in the methods +to do stuff as the service is started/stopped etc.: + +```python +class MyService(Service): + + async def on_start(self) -> None: + print('Im starting now') + + async def on_started(self) -> None: + print('Im ready') + + async def on_stop(self) -> None: + print('Im stopping now') +``` + +To start the service, call ``await service.start()``: + +```python +await service.start() +``` + +Or you can use ``mode.Worker`` (or a subclass of this) to start your +services-based asyncio program from the console: + +```python +if __name__ == '__main__': + import mode + worker = mode.Worker( + MyService(), + loglevel='INFO', + logfile=None, + daemon=False, + ) + worker.execute_from_commandline() +``` + +## It's a Graph! + +Services can start other services, coroutines, and background tasks. + +1) Starting other services using ``add_dependency``: + +```python + class MyService(Service): + + def __post_init__(self) -> None: + self.add_dependency(OtherService(loop=self.loop)) +``` + +1) Start a list of services using ``on_init_dependencies``: + +```python +class MyService(Service): + + def on_init_dependencies(self) -> None: + return [ + ServiceA(loop=self.loop), + ServiceB(loop=self.loop), + ServiceC(loop=self.loop), + ] +``` + +1) Start a future/coroutine (that will be waited on to complete on stop): + +```python + class MyService(Service): + + async def on_start(self) -> None: + self.add_future(self.my_coro()) + + async def my_coro(self) -> None: + print('Executing coroutine') +``` + +1) Start a background task: + +```python +class MyService(Service): + + @Service.task + async def _my_coro(self) -> None: + print('Executing coroutine') +``` + + +1) Start a background task that keeps running: + +```python +class MyService(Service): + + @Service.task + async def _my_coro(self) -> None: + while not self.should_stop: + # NOTE: self.sleep will wait for one second, or + # until service stopped/crashed. + await self.sleep(1.0) + print('Background thread waking up') +``` diff --git a/docs/index.md b/docs/index.md index f3bcb74f..cd84c730 100644 --- a/docs/index.md +++ b/docs/index.md @@ -21,232 +21,132 @@ restart and supervise. A service is just a class: ```python - class PageViewCache(Service): - redis: Redis = None +class PageViewCache(Service): + redis: Redis = None - async def on_start(self) -> None: - self.redis = connect_to_redis() + async def on_start(self) -> None: + self.redis = connect_to_redis() - async def update(self, url: str, n: int = 1) -> int: - return await self.redis.incr(url, n) + async def update(self, url: str, n: int = 1) -> int: + return await self.redis.incr(url, n) - async def get(self, url: str) -> int: - return await self.redis.get(url) + async def get(self, url: str) -> int: + return await self.redis.get(url) ``` - Services are started, stopped and restarted and have callbacks for those actions. It can start another service: ```python - class App(Service): - page_view_cache: PageViewCache = None +class App(Service): + page_view_cache: PageViewCache = None - async def on_start(self) -> None: - await self.add_runtime_dependency(self.page_view_cache) + async def on_start(self) -> None: + await self.add_runtime_dependency(self.page_view_cache) - @cached_property - def page_view_cache(self) -> PageViewCache: - return PageViewCache() + @cached_property + def page_view_cache(self) -> PageViewCache: + return PageViewCache() ``` It can include background tasks: ```python - class PageViewCache(Service): +class PageViewCache(Service): - @Service.timer(1.0) - async def _update_cache(self) -> None: - self.data = await cache.get('key') + @Service.timer(1.0) + async def _update_cache(self) -> None: + self.data = await cache.get('key') ``` Services that depends on other services actually form a graph that you can visualize. -Worker - Mode optionally provides a worker that you can use to start the program, - with support for logging, blocking detection, remote debugging and more. - - To start a worker add this to your program: - - if __name__ == '__main__': - from mode import Worker - Worker(Service(), loglevel="info").execute_from_commandline() - - Then execute your program to start the worker: - - ```sh - $ python examples/tutorial.py - [2018-03-27 15:47:12,159: INFO]: [^Worker]: Starting... - [2018-03-27 15:47:12,160: INFO]: [^-AppService]: Starting... - [2018-03-27 15:47:12,160: INFO]: [^--Websockets]: Starting... - STARTING WEBSOCKET SERVER - [2018-03-27 15:47:12,161: INFO]: [^--UserCache]: Starting... - [2018-03-27 15:47:12,161: INFO]: [^--Webserver]: Starting... - [2018-03-27 15:47:12,164: INFO]: [^--Webserver]: Serving on port 8000 - REMOVING EXPIRED USERS - REMOVING EXPIRED USERS - ``` - - To stop it hit :kbd:`Control-c`: - - ```sh - [2018-03-27 15:55:08,084: INFO]: [^Worker]: Stopping on signal received... - [2018-03-27 15:55:08,084: INFO]: [^Worker]: Stopping... - [2018-03-27 15:55:08,084: INFO]: [^-AppService]: Stopping... - [2018-03-27 15:55:08,084: INFO]: [^--UserCache]: Stopping... - REMOVING EXPIRED USERS - [2018-03-27 15:55:08,085: INFO]: [^Worker]: Gathering service tasks... - [2018-03-27 15:55:08,085: INFO]: [^--UserCache]: -Stopped! - [2018-03-27 15:55:08,085: INFO]: [^--Webserver]: Stopping... - [2018-03-27 15:55:08,085: INFO]: [^Worker]: Gathering all futures... - [2018-03-27 15:55:08,085: INFO]: [^--Webserver]: Closing server - [2018-03-27 15:55:08,086: INFO]: [^--Webserver]: Waiting for server to close handle - [2018-03-27 15:55:08,086: INFO]: [^--Webserver]: Shutting down web application - [2018-03-27 15:55:08,086: INFO]: [^--Webserver]: Waiting for handler to shut down - [2018-03-27 15:55:08,086: INFO]: [^--Webserver]: Cleanup - [2018-03-27 15:55:08,086: INFO]: [^--Webserver]: -Stopped! - [2018-03-27 15:55:08,086: INFO]: [^--Websockets]: Stopping... - [2018-03-27 15:55:08,086: INFO]: [^--Websockets]: -Stopped! - [2018-03-27 15:55:08,087: INFO]: [^-AppService]: -Stopped! - [2018-03-27 15:55:08,087: INFO]: [^Worker]: -Stopped! - ``` - -Beacons - The ``beacon`` object that we pass to services keeps track of the services - in a graph. - - They are not strictly required, but can be used to visualize a running - system, for example we can render it as a pretty graph. - - This requires you to have the `pydot` library and GraphViz - installed: - - ```sh - $ pip install pydot - ``` - - Let's change the app service class to dump the graph to an image - at startup: - - - ```python - class AppService(Service): - - async def on_start(self) -> None: - print('APP STARTING') - import pydot - import io - o = io.StringIO() - beacon = self.app.beacon.root or self.app.beacon - beacon.as_graph().to_dot(o) - graph, = pydot.graph_from_dot_data(o.getvalue()) - print('WRITING GRAPH TO image.png') - with open('image.png', 'wb') as fh: - fh.write(graph.create_png()) - ``` - - -## Creating a Service - -To define a service, simply subclass and fill in the methods -to do stuff as the service is started/stopped etc.: - -```python - class MyService(Service): - - async def on_start(self) -> None: - print('Im starting now') - - async def on_started(self) -> None: - print('Im ready') +### Worker - async def on_stop(self) -> None: - print('Im stopping now') -``` +Mode optionally provides a worker that you can use to start the program, +with support for logging, blocking detection, remote debugging and more. -To start the service, call ``await service.start()``: +To start a worker add this to your program: ```python - await service.start() +if __name__ == '__main__': + from mode import Worker + Worker(Service(), loglevel="info").execute_from_commandline() ``` -Or you can use ``mode.Worker`` (or a subclass of this) to start your -services-based asyncio program from the console: - -```python - if __name__ == '__main__': - import mode - worker = mode.Worker( - MyService(), - loglevel='INFO', - logfile=None, - daemon=False, - ) - worker.execute_from_commandline() -``` - -## It's a Graph! - -Services can start other services, coroutines, and background tasks. - -1) Starting other services using ``add_dependency``: - -```python - class MyService(Service): - - def __post_init__(self) -> None: - self.add_dependency(OtherService(loop=self.loop)) +Then execute your program to start the worker: + +```sh +$ python examples/tutorial.py +[2018-03-27 15:47:12,159: INFO]: [^Worker]: Starting... +[2018-03-27 15:47:12,160: INFO]: [^-AppService]: Starting... +[2018-03-27 15:47:12,160: INFO]: [^--Websockets]: Starting... +STARTING WEBSOCKET SERVER +[2018-03-27 15:47:12,161: INFO]: [^--UserCache]: Starting... +[2018-03-27 15:47:12,161: INFO]: [^--Webserver]: Starting... +[2018-03-27 15:47:12,164: INFO]: [^--Webserver]: Serving on port 8000 +REMOVING EXPIRED USERS +REMOVING EXPIRED USERS ``` -2) Start a list of services using ``on_init_dependencies``: - -```python - class MyService(Service): - - def on_init_dependencies(self) -> None: - return [ - ServiceA(loop=self.loop), - ServiceB(loop=self.loop), - ServiceC(loop=self.loop), - ] +To stop it hit `Control-c`: + +```sh +[2018-03-27 15:55:08,084: INFO]: [^Worker]: Stopping on signal received... +[2018-03-27 15:55:08,084: INFO]: [^Worker]: Stopping... +[2018-03-27 15:55:08,084: INFO]: [^-AppService]: Stopping... +[2018-03-27 15:55:08,084: INFO]: [^--UserCache]: Stopping... +REMOVING EXPIRED USERS +[2018-03-27 15:55:08,085: INFO]: [^Worker]: Gathering service tasks... +[2018-03-27 15:55:08,085: INFO]: [^--UserCache]: -Stopped! +[2018-03-27 15:55:08,085: INFO]: [^--Webserver]: Stopping... +[2018-03-27 15:55:08,085: INFO]: [^Worker]: Gathering all futures... +[2018-03-27 15:55:08,085: INFO]: [^--Webserver]: Closing server +[2018-03-27 15:55:08,086: INFO]: [^--Webserver]: Waiting for server to close handle +[2018-03-27 15:55:08,086: INFO]: [^--Webserver]: Shutting down web application +[2018-03-27 15:55:08,086: INFO]: [^--Webserver]: Waiting for handler to shut down +[2018-03-27 15:55:08,086: INFO]: [^--Webserver]: Cleanup +[2018-03-27 15:55:08,086: INFO]: [^--Webserver]: -Stopped! +[2018-03-27 15:55:08,086: INFO]: [^--Websockets]: Stopping... +[2018-03-27 15:55:08,086: INFO]: [^--Websockets]: -Stopped! +[2018-03-27 15:55:08,087: INFO]: [^-AppService]: -Stopped! +[2018-03-27 15:55:08,087: INFO]: [^Worker]: -Stopped! ``` -3) Start a future/coroutine (that will be waited on to complete on stop): +### Beacons -```python - class MyService(Service): - - async def on_start(self) -> None: - self.add_future(self.my_coro()) +The `beacon` object that we pass to services keeps track of the services +in a graph. - async def my_coro(self) -> None: - print('Executing coroutine') -``` +They are not strictly required, but can be used to visualize a running +system, for example we can render it as a pretty graph. -4) Start a background task: - -```python - class MyService(Service): +This requires you to have the `pydot` library and GraphViz +installed: - @Service.task - async def _my_coro(self) -> None: - print('Executing coroutine') +```sh +$ pip install pydot ``` +Let's change the app service class to dump the graph to an image +at startup: -5) Start a background task that keeps running: ```python - class MyService(Service): - - @Service.task - async def _my_coro(self) -> None: - while not self.should_stop: - # NOTE: self.sleep will wait for one second, or - # until service stopped/crashed. - await self.sleep(1.0) - print('Background thread waking up') +class AppService(Service): + + async def on_start(self) -> None: + print('APP STARTING') + import pydot + import io + o = io.StringIO() + beacon = self.app.beacon.root or self.app.beacon + beacon.as_graph().to_dot(o) + graph, = pydot.graph_from_dot_data(o.getvalue()) + print('WRITING GRAPH TO image.png') + with open('image.png', 'wb') as fh: + fh.write(graph.create_png()) ``` diff --git a/docs/references/mode.loop.eventlet.md b/docs/references/mode.loop.eventlet.md index 072f34a1..49887afa 100644 --- a/docs/references/mode.loop.eventlet.md +++ b/docs/references/mode.loop.eventlet.md @@ -5,4 +5,4 @@ !!! warning Importing this module directly will set the global event loop. - See :mod:`faust.loop` for more information. + See `faust.loop` for more information. diff --git a/docs/references/mode.loop.gevent.md b/docs/references/mode.loop.gevent.md index 8f0a229f..f23c4cb9 100644 --- a/docs/references/mode.loop.gevent.md +++ b/docs/references/mode.loop.gevent.md @@ -5,4 +5,4 @@ !!! warning Importing this module directly will set the global event loop. - See :mod:`faust.loop` for more information. + See `faust.loop` for more information. diff --git a/docs/references/mode.loop.uvloop.md b/docs/references/mode.loop.uvloop.md index c1f5044a..6cdf6e72 100644 --- a/docs/references/mode.loop.uvloop.md +++ b/docs/references/mode.loop.uvloop.md @@ -5,4 +5,4 @@ !!! warning Importing this module directly will set the global event loop. - See :mod:`faust.loop` for more information. + See `faust.loop` for more information. diff --git a/docs/references/mode.md b/docs/references/mode.md deleted file mode 100644 index 342308cf..00000000 --- a/docs/references/mode.md +++ /dev/null @@ -1,3 +0,0 @@ -# mode - -::: mode diff --git a/mkdocs.yml b/mkdocs.yml index 0d14e403..28c34726 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -4,19 +4,21 @@ site_description: avro, kafka, client, faust, schema theme: name: 'material' palette: - primary: black -# - scheme: default -# primary: blue grey -# accent: indigo -# toggle: -# icon: material/lightbulb -# name: Switch to dark mode -# - scheme: slate -# primary: blue grey -# accent: indigo -# toggle: -# icon: material/lightbulb-outline -# name: Switch to light mode + # Palette toggle for light mode + - scheme: default + primary: deep orange + accent: deep orange + toggle: + icon: material/brightness-7 + name: Switch to dark mode + + # Palette toggle for dark mode + - scheme: slate + primary: deep orange + accent: deep orange + toggle: + icon: material/brightness-4 + name: Switch to light mode features: - navigation.tabs - navigation.sections @@ -29,15 +31,16 @@ repo_name: faust-streaming/mode repo_url: https://github.com/faust-streaming/mode nav: - - Introduction: 'index.md' + - Mode: + - Introduction: 'index.md' + - Creating a Service: 'creating-service.md' - References: - 'Mode': - - mode: references/mode.md + - mode.services: references/mode.services.md - mode.debug: references/mode.debug.md - mode.exceptions: references/mode.exceptions.md - mode.locals: references/mode.locals.md - mode.proxy: references/mode.proxy.md - - mode.services: references/mode.services.md - mode.signals: references/mode.signals.md - mode.supervisors: references/mode.supervisors.md - mode.threads: references/mode.threads.md @@ -86,6 +89,7 @@ markdown_extensions: - pymdownx.mark - pymdownx.tilde - pymdownx.details + - pymdownx.magiclink - tables - attr_list - md_in_html diff --git a/mode/debug.py b/mode/debug.py index 7334ca9c..21f4b0e6 100644 --- a/mode/debug.py +++ b/mode/debug.py @@ -41,8 +41,11 @@ class BlockingDetector(Service): """Service that detects blocking code using alarm/itimer. Examples: - blockdetect = BlockingDetector(timeout=10.0) - await blockdetect.start() + + ```python + blockdetect = BlockingDetector(timeout=10.0) + await blockdetect.start() + ``` Keyword Arguments: timeout (Seconds): number of seconds that the event loop can diff --git a/mode/locals.py b/mode/locals.py index 07db83a8..30fe87ad 100644 --- a/mode/locals.py +++ b/mode/locals.py @@ -26,7 +26,7 @@ class XProxy(MutableMappingRole, AsyncContextManagerRole): Evaluation ========== -By default the callable passed to :class:`Proxy` will be evaluated +By default the callable passed to `Proxy` will be evaluated every time it is needed, so in the example above a new X will be created every time you access the underlying object: @@ -51,7 +51,7 @@ class XProxy(MutableMappingRole, AsyncContextManagerRole): ``` If you want the creation of the object to be lazy (created -when first needed), you can pass the `cache=True` argument to :class:`Proxy`: +when first needed), you can pass the `cache=True` argument to `Proxy`: ```sh >>> x = XProxy(create_real, cache=True) @@ -397,7 +397,7 @@ def __reduce__(self) -> Tuple: class AwaitableRole(Awaitable[T]): - """Role/Mixin for :class:`typing.Awaitable` proxy methods.""" + """Role/Mixin for `typing.Awaitable` proxy methods.""" def _get_awaitable(self) -> Awaitable[T]: obj = self._get_current_object() # type: ignore @@ -408,11 +408,11 @@ def __await__(self) -> Generator[Any, None, T]: class AwaitableProxy(Proxy[T], AwaitableRole[T]): - """Proxy to :class:`typing.Awaitable` object.""" + """Proxy to `typing.Awaitable` object.""" class CoroutineRole(Coroutine[T_co, T_contra, V_co]): - """Role/Mixin for :class:`typing.Coroutine` proxy methods.""" + """Role/Mixin for `typing.Coroutine` proxy methods.""" def _get_coroutine(self) -> Coroutine[T_co, T_contra, V_co]: obj = self._get_current_object() # type: ignore @@ -439,11 +439,11 @@ def close(self) -> None: class CoroutineProxy( Proxy[Coroutine[T_co, T_contra, V_co]], CoroutineRole[T_co, T_contra, V_co] ): - """Proxy to :class:`typing.Coroutine` object.""" + """Proxy to `typing.Coroutine` object.""" class AsyncIterableRole(AsyncIterable[T_co]): - """Role/Mixin for :class:`typing.AsyncIterable` proxy methods.""" + """Role/Mixin for `typing.AsyncIterable` proxy methods.""" def _get_iterable(self) -> AsyncIterable[T_co]: obj = self._get_current_object() # type: ignore @@ -454,11 +454,11 @@ def __aiter__(self) -> AsyncIterator[T_co]: class AsyncIterableProxy(Proxy[AsyncIterable[T_co]], AsyncIterableRole[T_co]): - """Proxy to :class:`typing.AsyncIterable` object.""" + """Proxy to `typing.AsyncIterable` object.""" class AsyncIteratorRole(AsyncIterator[T_co]): - """Role/Mixin for :class:`typing.AsyncIterator` proxy methods.""" + """Role/Mixin for `typing.AsyncIterator` proxy methods.""" def _get_iterator(self) -> AsyncIterator[T_co]: obj = self._get_current_object() # type: ignore @@ -472,11 +472,11 @@ def __anext__(self) -> Awaitable[T_co]: class AsyncIteratorProxy(Proxy[AsyncIterator[T_co]], AsyncIteratorRole[T_co]): - """Proxy to :class:`typing.AsyncIterator` object.""" + """Proxy to `typing.AsyncIterator` object.""" class AsyncGeneratorRole(AsyncGenerator[T_co, T_contra]): - """Role/Mixin for :class:`typing.AsyncGenerator` proxy methods.""" + """Role/Mixin for `typing.AsyncGenerator` proxy methods.""" def _get_generator(self) -> AsyncGenerator[T_co, T_contra]: obj = self._get_current_object() # type: ignore @@ -506,11 +506,11 @@ def __aiter__(self) -> AsyncGenerator[T_co, T_contra]: class AsyncGeneratorProxy( Proxy[AsyncGenerator[T_co, T_contra]], AsyncGeneratorRole[T_co, T_contra] ): - """Proxy to :class:`typing.AsyncGenerator` object.""" + """Proxy to `typing.AsyncGenerator` object.""" class SequenceRole(Sequence[T_co]): - """Role/Mixin for :class:`typing.Sequence` proxy methods.""" + """Role/Mixin for `typing.Sequence` proxy methods.""" def _get_sequence(self) -> Sequence[T_co]: obj = self._get_current_object() # type: ignore @@ -545,11 +545,11 @@ def __len__(self) -> int: class SequenceProxy(Proxy[Sequence[T_co]], SequenceRole[T_co]): - """Proxy to :class:`typing.Sequence` object.""" + """Proxy to `typing.Sequence` object.""" class MutableSequenceRole(SequenceRole[T], MutableSequence[T]): - """Role/Mixin for :class:`typing.MutableSequence` proxy methods.""" + """Role/Mixin for `typing.MutableSequence` proxy methods.""" def _get_sequence(self) -> MutableSequence[T]: obj = self._get_current_object() # type: ignore @@ -598,11 +598,11 @@ def __iadd__(self, x: Iterable[T]) -> MutableSequence[T]: class MutableSequenceProxy( Proxy[MutableSequence[T_co]], MutableSequenceRole[T_co] ): - """Proxy to :class:`typing.MutableSequence` object.""" + """Proxy to `typing.MutableSequence` object.""" class SetRole(AbstractSet[T_co]): - """Role/Mixin for :class:`typing.AbstractSet` proxy methods.""" + """Role/Mixin for `typing.AbstractSet` proxy methods.""" def _get_set(self) -> AbstractSet[T_co]: obj = self._get_current_object() # type: ignore @@ -646,11 +646,11 @@ def __len__(self) -> int: class SetProxy(Proxy[AbstractSet[T_co]], SetRole[T_co]): - """Proxy to :class:`typing.AbstractSet` object.""" + """Proxy to `typing.AbstractSet` object.""" class MutableSetRole(SetRole[T], MutableSet[T]): - """Role/Mixin for :class:`typing.MutableSet` proxy methods.""" + """Role/Mixin for `typing.MutableSet` proxy methods.""" def _get_set(self) -> MutableSet[T]: obj = self._get_current_object() # type: ignore @@ -685,11 +685,11 @@ def __isub__(self, s: AbstractSet[Any]) -> MutableSet[T]: class MutableSetProxy(Proxy[MutableSet[T_co]], MutableSetRole[T_co]): - """Proxy to :class:`typing.MutableSet` object.""" + """Proxy to `typing.MutableSet` object.""" class ContextManagerRole(ContextManager[T]): - """Role/Mixin for :class:`typing.ContextManager` proxy methods.""" + """Role/Mixin for `typing.ContextManager` proxy methods.""" def _get_context(self) -> ContextManager[T]: obj = self._get_current_object() # type: ignore @@ -703,11 +703,11 @@ def __exit__(self, *exc_info: Any) -> Any: class ContextManagerProxy(Proxy[ContextManager[T]], ContextManagerRole[T]): - """Proxy to :class:`typing.ContextManager` object.""" + """Proxy to `typing.ContextManager` object.""" class AsyncContextManagerRole(AsyncContextManager[T_co]): - """Role/Mixin for :class:`typing.AsyncContextManager` proxy methods.""" + """Role/Mixin for `typing.AsyncContextManager` proxy methods.""" def __aenter__(self) -> Awaitable[T_co]: obj = self._get_current_object() # type: ignore @@ -727,11 +727,11 @@ def __aexit__( class AsyncContextManagerProxy( Proxy[AsyncContextManager[T_co]], AsyncContextManagerRole[T_co] ): - """Proxy to :class:`typing.AsyncContextManager` object.""" + """Proxy to `typing.AsyncContextManager` object.""" class MappingRole(Mapping[KT, VT_co]): - """Role/Mixin for :class:`typing.Mapping` proxy methods.""" + """Role/Mixin for `typing.Mapping` proxy methods.""" def _get_mapping(self) -> Mapping[KT, VT_co]: obj = self._get_current_object() # type: ignore @@ -769,11 +769,11 @@ def __len__(self) -> int: class MappingProxy(Proxy[Mapping[KT, VT_co]], MappingRole[KT, VT_co]): - """Proxy to :class:`typing.Mapping` object.""" + """Proxy to `typing.Mapping` object.""" class MutableMappingRole(MappingRole[KT, VT], MutableMapping[KT, VT]): - """Role/Mixin for :class:`typing.MutableMapping` proxy methods.""" + """Role/Mixin for `typing.MutableMapping` proxy methods.""" def _get_mapping(self) -> MutableMapping[KT, VT]: obj = self._get_current_object() # type: ignore @@ -819,11 +819,11 @@ def update(self, *args: Any, **kwargs: Any) -> None: class MutableMappingProxy( Proxy[MutableMapping[KT, VT]], MutableMappingRole[KT, VT] ): - """Proxy to :class:`typing.MutableMapping` object.""" + """Proxy to `typing.MutableMapping` object.""" class CallableRole: - """Role/Mixin for :class:`typing.Callable` proxy methods.""" + """Role/Mixin for `typing.Callable` proxy methods.""" def _get_callable(self) -> Callable: obj = self._get_current_object() # type: ignore @@ -834,7 +834,7 @@ def __call__(self, *args: Any, **kwargs: Any) -> Any: class CallableProxy(Proxy[Callable], CallableRole): - """Proxy to :class:`typing.Callable` object.""" + """Proxy to `typing.Callable` object.""" def maybe_evaluate(obj: Any) -> Any: diff --git a/mode/loop/__init__.py b/mode/loop/__init__.py index 4ebec3ef..d964cbdf 100644 --- a/mode/loop/__init__.py +++ b/mode/loop/__init__.py @@ -6,14 +6,14 @@ The choices available are: aio **default** - Normal :mod:`asyncio` event loop policy. + Normal `asyncio` event loop policy. ### eventlet -Use :pypi:`eventlet` as the event loop. +Use [`eventlet`](https://pypi.org/project/eventlet) as the event loop. -This uses :pypi:`aioeventlet` and will apply the -:pypi:`eventlet` monkey-patches. +This uses [`aioeventlet`](https://pypi.org/project/aioeventlet) and will apply the +[`eventlet`](https://pypi.org/project/eventlet) monkey-patches. To enable execute the following as the first thing that happens when your program starts (e.g. add it as the top import of your @@ -26,10 +26,10 @@ ### gevent -Use :pypi:`gevent` as the event loop. +Use [`gevent`](https://pypi.org/project/gevent) as the event loop. -This uses :pypi:`aiogevent` (+modifications) and will apply the -:pypi:`gevent` monkey-patches. +This uses [`aiogevent`](https://pypi.org/project/aiogevent) (+modifications) and will apply the +[`gevent`](https://pypi.org/project/gevent) monkey-patches. This choice enables you to run blocking Python code as if they have invisible `async/await` syntax around it (NOTE: C extensions are @@ -46,7 +46,7 @@ ### uvloop -Event loop using :pypi:`uvloop`. +Event loop using [`uvloop`](https://pypi.org/project/uvloop). To enable execute the following as the first thing that happens when your program starts (e.g. add it as the top import of your diff --git a/mode/loop/_gevent_loop.py b/mode/loop/_gevent_loop.py index 133fc71c..8dc86e5d 100644 --- a/mode/loop/_gevent_loop.py +++ b/mode/loop/_gevent_loop.py @@ -7,7 +7,7 @@ class Loop(gevent.core.loop): # type: ignore - """Gevent core event loop modifed to support :mod:`asyncio`.""" + """Gevent core event loop modifed to support `asyncio`.""" _aioloop_loop = None diff --git a/mode/loop/eventlet.py b/mode/loop/eventlet.py index 112f1bbf..38575a81 100644 --- a/mode/loop/eventlet.py +++ b/mode/loop/eventlet.py @@ -1,4 +1,4 @@ -"""Enable :pypi:`eventlet` support for :mod:`asyncio`.""" +"""Enable [`eventlet`](https://pypi.org/project/eventlet) support for `asyncio`.""" import asyncio import os diff --git a/mode/loop/gevent.py b/mode/loop/gevent.py index 02a9652b..fce643ae 100644 --- a/mode/loop/gevent.py +++ b/mode/loop/gevent.py @@ -1,4 +1,4 @@ -"""Enable :pypi:`gevent` support for :mod:`asyncio`.""" +"""Enable [`gevent`](https://pypi.org/project/gevent) support for `asyncio`.""" import asyncio import os diff --git a/mode/loop/uvloop.py b/mode/loop/uvloop.py index 3304aac2..42f3e046 100644 --- a/mode/loop/uvloop.py +++ b/mode/loop/uvloop.py @@ -1,4 +1,4 @@ -"""Enable :pypi:`uvloop` as the event loop for :mod:`asyncio`.""" +"""Enable [`uvloop`](https://pypi.org/project/uvloop) as the event loop for `asyncio`.""" import asyncio diff --git a/mode/services.py b/mode/services.py index c2fdaf2a..e8850f11 100644 --- a/mode/services.py +++ b/mode/services.py @@ -62,7 +62,7 @@ class WaitResults(NamedTuple): class WaitResult(NamedTuple): - """Return value of :meth:`Service.wait`.""" + """Return value of `Service.wait`.""" #: Return value of the future we were waiting for. result: Any @@ -443,11 +443,11 @@ def timer( """Background timer executing every ``n`` seconds. ```python - class S(Service): + class S(Service): - @Service.timer(1.0) - async def background_timer(self): - print('Waking up') + @Service.timer(1.0) + async def background_timer(self): + print('Waking up') ``` """ _interval = want_seconds(interval) @@ -1063,7 +1063,7 @@ async def itertimer( """Sleep `interval` seconds for every iteration. This is an async iterator that takes advantage - of :func:`~mode.timers.Timer` to monitor drift and timer + of `~mode.timers.Timer` to monitor drift and timer overlap. Uses `Service.sleep` so exits fast when the service is diff --git a/mode/supervisors.py b/mode/supervisors.py index c85a84c9..89a8846b 100644 --- a/mode/supervisors.py +++ b/mode/supervisors.py @@ -90,7 +90,7 @@ def _contribute_to_service(self, service: ServiceT) -> None: # Setting the service.supervisor attribute here means calling # `await service.crash(exc)` won't traverse the tree, crash # every parent of the service, until it hits Worker terminating - # the running program abruptly. See :class:`CrashingSupervisor`. + # the running program abruptly. See `CrashingSupervisor`. service.supervisor = self def discard(self, *services: ServiceT) -> None: diff --git a/mode/threads.py b/mode/threads.py index 8a5c5653..2dd0aeb7 100644 --- a/mode/threads.py +++ b/mode/threads.py @@ -3,8 +3,9 @@ Will use the default thread pool executor (``loop.set_default_executor()``), unless you specify a specific executor instance. -Note: To stop something using the thread's loop, you have to -use the ``on_thread_stop`` callback instead of the on_stop callback. +!!! note + To stop something using the thread's loop, you have to + use the ``on_thread_stop`` callback instead of the on_stop callback. """ import asyncio diff --git a/mode/timers.py b/mode/timers.py index 01997a7d..4d7a6138 100644 --- a/mode/timers.py +++ b/mode/timers.py @@ -174,9 +174,9 @@ def timer_intervals( # XXX deprecated ) -> Iterator[float]: """Generate timer sleep times. - Note: This function is deprecated, please use :func:`itertimer` - instead (this function also sleeps and calculates sleep time correctly.) - + !!! note + This function is deprecated, please use `itertimer` + instead (this function also sleeps and calculates sleep time correctly.) """ state = Timer( interval, diff --git a/mode/types/services.py b/mode/types/services.py index 32f6f269..7f69a5f4 100644 --- a/mode/types/services.py +++ b/mode/types/services.py @@ -1,4 +1,4 @@ -"""Type classes for :mod:`mode.services`.""" +"""Type classes for `mode.services`.""" import abc import asyncio @@ -48,7 +48,7 @@ class ServiceT(AsyncContextManager): """Abstract type for an asynchronous service that can be started/stopped. See Also: - :class:`mode.Service`. + `mode.Service`. """ Diag: Type[DiagT] diff --git a/mode/types/signals.py b/mode/types/signals.py index 9ba009f6..0fc43cd3 100644 --- a/mode/types/signals.py +++ b/mode/types/signals.py @@ -1,4 +1,4 @@ -"""Type classes for :mod:`mode.signals`.""" +"""Type classes for `mode.signals`.""" import abc import asyncio diff --git a/mode/types/supervisors.py b/mode/types/supervisors.py index 0fbe2bd3..f1c00996 100644 --- a/mode/types/supervisors.py +++ b/mode/types/supervisors.py @@ -1,4 +1,4 @@ -"""Type classes for :mod:`mode.supervisors`.""" +"""Type classes for `mode.supervisors`.""" import abc import typing diff --git a/mode/utils/aiter.py b/mode/utils/aiter.py index b285f143..f7cf0dd5 100644 --- a/mode/utils/aiter.py +++ b/mode/utils/aiter.py @@ -112,7 +112,7 @@ async def __anext__(self) -> int: class arange(AsyncIterable[int]): - """Async generator that counts like :class:`range`.""" + """Async generator that counts like `range`.""" def __init__( self, *slice_args: Optional[int], **slice_kwargs: Optional[int] diff --git a/mode/utils/collections.py b/mode/utils/collections.py index 992f60d0..2ebfc909 100644 --- a/mode/utils/collections.py +++ b/mode/utils/collections.py @@ -78,7 +78,7 @@ class LazySettings: ... class Heap(MutableSequence[T]): - """Generic interface to :mod:`heapq`.""" + """Generic interface to `heapq`.""" def __init__(self, data: Optional[Sequence[T]] = None) -> None: self.data = list(data or []) @@ -104,14 +104,14 @@ def pushpop(self, item: T) -> T: """Push item on the heap, then pop and return from the heap. The combined action runs more efficiently than - :meth:`push` followed by a separate call to :meth:`pop`. + `push` followed by a separate call to `pop`. """ return heappushpop(self.data, item) def replace(self, item: T) -> T: """Pop and return the current smallest value, and add the new item. - This is more efficient than :meth`pop` followed by :meth:`push`, + This is more efficient than :meth`pop` followed by `push`, and can be more appropriate when using a fixed-size heap. Note that the value returned may be larger than item! @@ -182,7 +182,7 @@ def __len__(self) -> int: class FastUserDict(MutableMapping[KT, VT]): """Proxy to dict. - Like :class:`collection.UserDict` but reimplements some methods + Like `collection.UserDict` but reimplements some methods for better performance when the underlying dictionary is a real dict. """ @@ -650,7 +650,9 @@ def raw_update(self, *args: Any, **kwargs: Any) -> None: class AttributeDictMixin: """Mixin for Mapping interface that adds attribute access. - I.e., `d.key -> d[key]`). + Example: + + `d.key` -> `d[key]` """ def __getattr__(self, k: str) -> Any: @@ -663,7 +665,11 @@ def __getattr__(self, k: str) -> Any: ) from err def __setattr__(self, key: str, value: Any) -> None: - """`d[key] = value -> d.key = value`.""" + """ + ```python + d[key] = value -> d.key = value + ``` + """ self[key] = value @@ -674,8 +680,8 @@ class AttributeDict(dict, AttributeDictMixin): class DictAttribute(MutableMapping[str, VT], MappingViewProxy): """Dict interface to attributes. - `obj[k] -> obj.k` - `obj[k] = val -> obj.k = val` + - `obj[k]` -> `obj.k` + - `obj[k] = val` -> `obj.k = val` """ obj: Any = None diff --git a/mode/utils/compat.py b/mode/utils/compat.py index bf3d7222..80252774 100644 --- a/mode/utils/compat.py +++ b/mode/utils/compat.py @@ -23,7 +23,7 @@ def isatty(fh: IO) -> bool: """Return True if fh has a controlling terminal. Notes: - Use with e.g. :data:`sys.stdin`. + Use with e.g. `sys.stdin`. """ try: return fh.isatty() diff --git a/mode/utils/futures.py b/mode/utils/futures.py index 39c6bfba..0776c14c 100644 --- a/mode/utils/futures.py +++ b/mode/utils/futures.py @@ -121,7 +121,7 @@ def __get__(self, obj: Any, type: Optional[Type] = None) -> Any: def done_future( result: Any = None, *, loop: Optional[asyncio.AbstractEventLoop] = None ) -> asyncio.Future: - """Return :class:`asyncio.Future` that is already evaluated.""" + """Return `asyncio.Future` that is already evaluated.""" f = ( loop or asyncio.get_event_loop_policy().get_event_loop() ).create_future() @@ -167,7 +167,7 @@ def maybe_set_result(fut: Optional[asyncio.Future], result: Any) -> bool: def notify(fut: Optional[asyncio.Future], result: Any = None) -> None: - """Set :class:`asyncio.Future` result if future exists and is not done.""" + """Set `asyncio.Future` result if future exists and is not done.""" # can be used to turn a Future into a lockless, single-consumer condition, # for multi-consumer use asyncio.Condition if fut is not None and not fut.done(): diff --git a/mode/utils/imports.py b/mode/utils/imports.py index f03a54f4..afb00681 100644 --- a/mode/utils/imports.py +++ b/mode/utils/imports.py @@ -149,7 +149,7 @@ def _ensure_identifier(path: str, full: str) -> None: class ParsedSymbol(NamedTuple): - """Tuple returned by :func:`parse_symbol`.""" + """Tuple returned by `parse_symbol`.""" module_name: Optional[str] attribute_name: Optional[str] @@ -162,7 +162,7 @@ def parse_symbol( strict_separator: str = ":", relative_separator: str = ".", ) -> ParsedSymbol: - """Parse :func:`symbol_by_name` argument into components. + """Parse `symbol_by_name` argument into components. Returns: ParsedSymbol: Tuple of ``(module_name, attribute_name)`` @@ -254,20 +254,19 @@ def symbol_by_name( Examples: - ```sh - >>> symbol_by_name('mazecache.backends.redis:RedisBackend') - - - >>> symbol_by_name('default', { - ... 'default': 'mazecache.backends.redis:RedisBackend'}) - + ```sh + >>> symbol_by_name('mazecache.backends.redis:RedisBackend') + - # Does not try to look up non-string names. - >>> from mazecache.backends.redis import RedisBackend - >>> symbol_by_name(RedisBackend) is RedisBackend - True - ``` + >>> symbol_by_name('default', { + ... 'default': 'mazecache.backends.redis:RedisBackend'}) + + # Does not try to look up non-string names. + >>> from mazecache.backends.redis import RedisBackend + >>> symbol_by_name(RedisBackend) is RedisBackend + True + ``` """ # This code was copied from kombu.utils.symbol_by_name imp = importlib.import_module if imp is None else imp @@ -412,7 +411,7 @@ def import_from_cwd( def smart_import(path: str, imp: Any = None) -> Any: - """Import module if module, otherwise same as :func:`symbol_by_name`.""" + """Import module if module, otherwise same as `symbol_by_name`.""" imp = importlib.import_module if imp is None else imp if ":" in path: # Path includes attribute so can just jump diff --git a/mode/utils/locals.py b/mode/utils/locals.py index bc08f60d..955b1f1a 100644 --- a/mode/utils/locals.py +++ b/mode/utils/locals.py @@ -1,4 +1,4 @@ -"""Implements thread-local stack using :class:`ContextVar` (:pep:`567`). +"""Implements thread-local stack using `ContextVar` (:pep:`567`). This is a reimplementation of the local stack as used by Flask, Werkzeug, Celery, and other libraries to keep a thread-local stack of objects. diff --git a/mode/utils/locks.py b/mode/utils/locks.py index 9a331aea..be89882c 100644 --- a/mode/utils/locks.py +++ b/mode/utils/locks.py @@ -1,6 +1,6 @@ """Modern versions of asyncio.locks. -asyncio primitives call get_event_loop() in __init__, +asyncio primitives call `get_event_loop()` in __init__, which makes them unsuitable for use in programs that don't want to pass the loop around. """ diff --git a/mode/utils/logging.py b/mode/utils/logging.py index 64b287ca..bf8a4c38 100644 --- a/mode/utils/logging.py +++ b/mode/utils/logging.py @@ -305,7 +305,7 @@ def formatter(fun: FormatterHandler) -> FormatterHandler: def formatter2(fun: FormatterHandler2) -> FormatterHandler2: """Register formatter for logging positional args. - Like :func:`formatter` but the handler accepts + Like `formatter` but the handler accepts two arguments instead of one: ``(arg, logrecord)``. Passing the log record as additional argument expands @@ -333,7 +333,7 @@ def format(self, record: logging.LogRecord) -> str: class ExtensionFormatter(colorlog.TTYColoredFormatter): """Formatter that can register callbacks to format args. - Extends :pypi:`colorlog`. + Extends [`colorlog`](https://pypi.org/project/colorlog). """ def __init__(self, stream: Optional[IO] = None, **kwargs: Any) -> None: @@ -585,7 +585,7 @@ def cry( def print_task_name(task: asyncio.Task, file: IO) -> None: - """Print name of :class:`asyncio.Task` in tracebacks.""" + """Print name of `asyncio.Task` in tracebacks.""" coro = task._coro # type: ignore wrapped = getattr(task, "__wrapped__", None) coro_name = getattr(coro, "__name__", None) @@ -610,7 +610,7 @@ class LogMessage(NamedTuple): class flight_recorder(ContextManager, LogSeverityMixin): - """Flight Recorder context for use with :keyword:`with` statement. + """Flight Recorder context for use with `with` statement. This is a logging utility to log stuff only when something times out. @@ -650,7 +650,7 @@ def _background_refresh(self) -> None: on_timeout.info(f'-redis_client.get({POSTS_KEY!r})') ``` - If the body of this :keyword:`with` statement completes before the + If the body of this `with` statement completes before the timeout, the logs are forgotten about and never emitted -- if it takes more than ten seconds to complete, we will see these messages in the log: @@ -863,7 +863,7 @@ def _safewrap_handlers(self) -> None: def _safewrap_handler(self, handler: logging.Handler) -> None: # Make the logger handlers dump internal errors to - # :data:`sys.__stderr__` instead of :data:`sys.stderr` to circumvent + # `sys.__stderr__` instead of `sys.stderr` to circumvent # infinite loops. class WithSafeHandleError(logging.Handler): def handleError(self, record: logging.LogRecord) -> None: @@ -983,7 +983,7 @@ def redirect_stdouts( stdout: bool = True, stderr: bool = True, ) -> Iterator[FileLogProxy]: - """Redirect :data:`sys.stdout` and :data:`sys.stdout` to logger.""" + """Redirect `sys.stdout` and `sys.stdout` to logger.""" proxy = FileLogProxy(logger, severity=severity) if stdout: sys.stdout = proxy diff --git a/mode/utils/mocks.py b/mode/utils/mocks.py index 1ee8d4d4..5cd4eb32 100644 --- a/mode/utils/mocks.py +++ b/mode/utils/mocks.py @@ -36,7 +36,7 @@ def __repr__(self) -> str: @contextmanager def patch_module(*names: str, new_callable: Any = MagicMock) -> Iterator: - """Mock one or modules such that every attribute is a :class:`Mock`.""" + """Mock one or modules such that every attribute is a `Mock`.""" prev = {} class MockModule(types.ModuleType): diff --git a/mode/utils/objects.py b/mode/utils/objects.py index f659b56e..8a515809 100644 --- a/mode/utils/objects.py +++ b/mode/utils/objects.py @@ -123,7 +123,7 @@ class _UsingKwargsInNew(_InitSubclassCheck, ident=909): ... class InvalidAnnotation(Exception): - """Raised by :func:`annotations` when encountering an invalid type.""" + """Raised by `annotations` when encountering an invalid type.""" @total_ordering @@ -152,7 +152,7 @@ class KeywordReduce: """Mixin class for objects that can be "pickled". "Pickled" means the object can be serialized using the Python binary - serializer -- the :mod:`pickle` module. + serializer -- the `pickle` module. Python objects are made pickleable through defining the ``__reduce__`` method, that returns a tuple of: @@ -265,11 +265,11 @@ def annotations( :exc:`InvalidAnnotation` (does not test for subclasses). alias_types: Mapping of original type to replacement type. skip_classvar: Skip attributes annotated with - :class:`typing.ClassVar`. + `typing.ClassVar`. globalns: Global namespace to use when evaluating forward - references (see :class:`typing.ForwardRef`). + references (see `typing.ForwardRef`). localns: Local namespace to use when evaluating forward - references (see :class:`typing.ForwardRef`). + references (see `typing.ForwardRef`). Returns: Tuple[FieldMapping, DefaultsMapping]: Tuple with two dictionaries, diff --git a/mode/utils/queues.py b/mode/utils/queues.py index 2f18aa74..c09bca75 100644 --- a/mode/utils/queues.py +++ b/mode/utils/queues.py @@ -1,4 +1,4 @@ -"""Queue utilities - variations of :class:`asyncio.Queue`.""" +"""Queue utilities - variations of `asyncio.Queue`.""" import asyncio import math @@ -24,7 +24,7 @@ class FlowControlEvent: - """Manage flow control :class:`FlowControlQueue` instances. + """Manage flow control `FlowControlQueue` instances. The FlowControlEvent manages flow in one or many queue instances at the same time. @@ -87,7 +87,7 @@ def __init__( self._queues = WeakSet() def manage_queue(self, queue: "FlowControlQueue") -> None: - """Add :class:`FlowControlQueue` to be cleared on resume.""" + """Add `FlowControlQueue` to be cleared on resume.""" self._queues.add(queue) def suspend(self) -> None: @@ -115,10 +115,10 @@ async def acquire(self) -> None: class FlowControlQueue(asyncio.Queue): - """:class:`asyncio.Queue` managed by :class:`FlowControlEvent`. + """`asyncio.Queue` managed by `FlowControlEvent`. See Also: - :class:`FlowControlEvent`. + `FlowControlEvent`. """ pressure_high_ratio = 1.25 # divided by diff --git a/mode/utils/text.py b/mode/utils/text.py index a109722a..3605c3c1 100644 --- a/mode/utils/text.py +++ b/mode/utils/text.py @@ -47,7 +47,7 @@ def isatty(fh: IO) -> bool: """Return True if fh has a controlling terminal. Notes: - Use with e.g. :data:`sys.stdin`. + Use with e.g. `sys.stdin`. """ try: return fh.isatty() @@ -79,21 +79,23 @@ def didyoumean( """Generate message with helpful list of alternatives. Examples: - >>> raise Exception(f'Unknown mode: {mode}! {didyoumean(modes, mode)}') - >>> didyoumean(['foo', 'bar', 'baz'], 'boo') - 'Did you mean foo?' + ```sh + >>> raise Exception(f'Unknown mode: {mode}! {didyoumean(modes, mode)}') + + >>> didyoumean(['foo', 'bar', 'baz'], 'boo') + 'Did you mean foo?' - >>> didyoumean(['foo', 'moo', 'bar'], 'boo') - 'Did you mean one of foo, moo?' + >>> didyoumean(['foo', 'moo', 'bar'], 'boo') + 'Did you mean one of foo, moo?' - >>> didyoumean(['foo', 'moo', 'bar'], 'xxx') - '' + >>> didyoumean(['foo', 'moo', 'bar'], 'xxx') + '' + ``` Arguments: haystack: List of all available choices. needle: What the user provided. - fmt_many: String format returned when there are more than one alternative. Default is: ``"Did you mean one of {alt}?"``. fmt_one: String format returned when there's a single fuzzy match. @@ -127,7 +129,7 @@ def enumeration( ```sh >>> enumeration(['x', 'y', '...']) - "1) x\n2) y\n3) ..." + "1) x\\n2) y\\n3) ..." ``` """ return sep.join( @@ -224,7 +226,7 @@ def abbr_fqdn(origin: str, name: str, *, prefix: str = "") -> str: 'examples.other.foo' ``` - :func:`shorten_fqdn` is similar, but will always shorten a too long name, + `shorten_fqdn` is similar, but will always shorten a too long name, abbr_fqdn will only remove the origin portion of the name. """ if name.startswith(origin): diff --git a/mode/utils/times.py b/mode/utils/times.py index b47ea855..d22341a4 100644 --- a/mode/utils/times.py +++ b/mode/utils/times.py @@ -37,7 +37,7 @@ else: TIME_MONOTONIC = time.monotonic -#: Seconds can be expressed as float or :class:`~datetime.timedelta`, +#: Seconds can be expressed as float or `~datetime.timedelta`, Seconds = Union[timedelta, float, str] @@ -243,7 +243,7 @@ def rate_limit( @singledispatch def want_seconds(s: float) -> float: - """Convert :data:`Seconds` to float.""" + """Convert `Seconds` to float.""" return s @@ -271,7 +271,7 @@ def humanize_seconds( For example, 60 becomes "1 minute", and 7200 becomes "2 hours". Arguments: - secs: Seconds to format (as :class:`float` or :class:`int`). + secs: Seconds to format (as `float` or `int`). prefix (str): can be used to add a preposition to the output (e.g., 'in' will give 'in 1 second', but add nothing to 'now'). suffix (str): same as prefix, adds suffix unless 'now'. diff --git a/mode/utils/tracebacks.py b/mode/utils/tracebacks.py index 74a77fd9..b12067b3 100644 --- a/mode/utils/tracebacks.py +++ b/mode/utils/tracebacks.py @@ -30,7 +30,7 @@ def print_task_stack( limit: int = DEFAULT_MAX_FRAMES, capture_locals: bool = False, ) -> None: - """Print the stack trace for an :class:`asyncio.Task`.""" + """Print the stack trace for an `asyncio.Task`.""" print(f"Stack for {task!r} (most recent call last):", file=file) tb = Traceback.from_task(task, limit=limit) print_list( @@ -89,7 +89,7 @@ def format_task_stack( limit: int = DEFAULT_MAX_FRAMES, capture_locals: bool = False, ) -> str: - """Format :class:`asyncio.Task` stack trace as a string.""" + """Format `asyncio.Task` stack trace as a string.""" f = io.StringIO() print_task_stack(task, file=f, limit=limit, capture_locals=capture_locals) return f.getvalue() diff --git a/mode/utils/trees.py b/mode/utils/trees.py index 53ab4975..b2a8645f 100644 --- a/mode/utils/trees.py +++ b/mode/utils/trees.py @@ -119,7 +119,7 @@ def walk(self) -> Iterator[NodeT[T]]: node = node.parent def as_graph(self) -> DependencyGraphT: - """Convert to :class:`~mode.utils.graphs.DependencyGraph`.""" + """Convert to `~mode.utils.graphs.DependencyGraph`.""" graph = DependencyGraph() stack: Deque[NodeT] = Deque([self]) while stack: diff --git a/mode/utils/types/graphs.py b/mode/utils/types/graphs.py index d5de3830..a1aec760 100644 --- a/mode/utils/types/graphs.py +++ b/mode/utils/types/graphs.py @@ -1,4 +1,4 @@ -"""Type classes for :mod:`mode.utils.graphs`.""" +"""Type classes for `mode.utils.graphs`.""" import abc from typing import ( diff --git a/mode/utils/types/trees.py b/mode/utils/types/trees.py index c2fc0f3b..5d38672d 100644 --- a/mode/utils/types/trees.py +++ b/mode/utils/types/trees.py @@ -1,4 +1,4 @@ -"""Type classes for :mod:`mode.utils.trees`.""" +"""Type classes for `mode.utils.trees`.""" import abc from typing import Any, Generic, Iterator, List, Optional, TypeVar, Union diff --git a/requirements-docs.txt b/requirements-docs.txt index f67f9879..2a5a7446 100644 --- a/requirements-docs.txt +++ b/requirements-docs.txt @@ -1,8 +1,3 @@ -six -sphinx<6.0.0 -sphinx_celery>=1.4.8 -sphinx-autodoc-annotation -alabaster -babel -sphinx-autobuild # TODO: check need -sphinx2rst>=1.0 # TODO: check need +mkdocs>=1.5.3 +mkdocs-material>=9.5.13 +mkdocstrings>=0.24.1