From 69f3e383f8e59fdbdc00ef10c1ac63a38a1b4f58 Mon Sep 17 00:00:00 2001 From: mejroslav Date: Thu, 10 Aug 2023 10:43:27 +0200 Subject: [PATCH 1/5] Rename sections --- mkdocs.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mkdocs.yml b/mkdocs.yml index e971214f5..a1192e167 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -159,14 +159,14 @@ nav: - How-Tos: - Creating a microservice with REST API: how-tos/03_rest_api.md - - Documentation: + - Reference: - Application: reference/application/reference.md - Modules and Services: reference/modules_services/reference.md - Web Service: - Web server: reference/web/web-server.md - TLS/SSL: reference/web/tls.md - - Authorization and multitenancy: reference/web/authorization_multitenancy.md + - Authorization and Multi-tenancy: reference/web/authorization_multitenancy.md - Cross-Origin Resource Sharing: reference/web/cors.md - Configuration: reference/config/reference.md - Logging: reference/logging/reference.md From e03700919c21dec0ef42d177e04a26a55f63cfab Mon Sep 17 00:00:00 2001 From: mejroslav Date: Thu, 10 Aug 2023 13:01:34 +0200 Subject: [PATCH 2/5] Formatting --- asab/application.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/asab/application.py b/asab/application.py index 6e08ef1b2..3f4752d37 100644 --- a/asab/application.py +++ b/asab/application.py @@ -553,19 +553,19 @@ def _register_service(self, service: asab.Service): async def initialize(self): """ - This method is called during the application _init-time_. It is intended to be overridden by the user. + This method is called during the application *init-time*. It is intended to be overridden by the user. """ pass async def main(self): """ - This method is called during the application _run-time_. It is intended to be overridden by the user. + This method is called during the application *run-time*. It is intended to be overridden by the user. """ pass async def finalize(self): """ - This method is called during the application _exit-time_. It is intended to be overridden by the user. + This method is called during the application *exit-time*. It is intended to be overridden by the user. """ pass From 22ff1f2098b32a48ca812f99836426ec2218ffcb Mon Sep 17 00:00:00 2001 From: mejroslav Date: Thu, 10 Aug 2023 13:02:03 +0200 Subject: [PATCH 3/5] Refactor Application lifecycle --- docs/reference/application/reference.md | 181 +++++++++++------------- 1 file changed, 84 insertions(+), 97 deletions(-) diff --git a/docs/reference/application/reference.md b/docs/reference/application/reference.md index 77a4e55cb..62c005932 100644 --- a/docs/reference/application/reference.md +++ b/docs/reference/application/reference.md @@ -3,28 +3,57 @@ The `asab.Application` class maintains the global application state. You can provide your own implementation by creating a subclass. There should be only one `Application` object in the process. -!!! example "Creating a new asab application:" +!!! example "Creating a new ASAB application:" - ```python - import asab + To create a new ASAB application, just create a subclass of `asab.Application` object and use the `run()` method: - class MyApplication(asab.Application): - pass + ```python title='app.py' + import asab - if __name__ == '__main__': - app = MyApplication() - app.run() - ``` + class MyApplication(asab.Application): + pass -!!! example "Direct use of `Application` object:" + if __name__ == '__main__': + app = MyApplication() + app.run() + ``` - ``` python - import asab + Then run the application from your terminal - if __name__ == '__main__': - app = asab.Application() - app.run() - ``` + ``` shell + python3 app.py + ``` + + and you should see the following output: + + ``` + NOTICE asab.application is ready. + ``` + + The app will be running until you stop it by `Ctrl+C`. + + To create an application that performs some operations and then stops, use the `stop()` method. + + ```python title='app_that_terminates.py' + import asab + + class MyApplication(asab.Application): + async def main(self): + print("Hello world!") + self.stop() + + if __name__ == '__main__': + app = MyApplication() + app.run() + ``` + + with the output: + + ``` + NOTICE asab.application is ready. + Hello world! + NOTICE asab.application [sd exit_code="0"] is exiting ... + ``` ## Application Lifecycle @@ -43,114 +72,72 @@ and exit-time. ### Init-time -The init-time happens during `Application` constructor call. -The Publish-Subscribe message `Application.init!` is published during init-time. -The `Config` is loaded during init-time. +The init-time happens during `Application` constructor call. +At this time: -The application object executes asynchronous callback `Application.initialize()`, which can be overridden by an user. +- [Configuration](/reference/config/reference) and [command line arguments](#command-line-parser) are loaded and [`asab.Config`](/reference/config/reference/#asab.Config) object is accessed. +- Asynchronous callback `Application.initialize()` is executed. +- [Application housekeeping](/reference/pubsub/reference/#housekeeping) is scheduled. +- [Publish-Subscribe](/reference/pubsub/reference/#well-known-messages) message **Application.init!** is published. -``` python -class MyApplication(asab.Application): - async def initialize(self): - # Custom initialization - from module_sample import Module - self.add_module(Module) -``` - -### Run-time - -The run-time starts after all the modules and services are loaded. This is where the application spends the most time typically. -The Publish-Subscribe message `Application.run!` is published when run-time begins. -The method returns the value of `Application.ExitCode`. - -The application object executes asynchronous callback -`Application.main()`, which can be overridden. If `main()` method is -completed without calling `stop()`, then the application server will run -forever (this is the default behaviour). +The asynchronous callback `Application.initialize()` is intended to be overridden by an user. +This is where you typically load Modules and register Services, see [Modules and Services](/reference/modules_services/reference) section. ``` python class MyApplication(asab.Application): - async def main(self): - print("Hello world!") - self.stop() + async def initialize(self): + # Custom initialization + from module_sample import Module + self.add_module(Module) ``` -The method `Application.stop()` gracefully terminates the run-time and -commence the exit-time. This method is automatically called by `SIGINT` -and `SIGTERM`. It also includes a response to `Ctrl-C` on UNIX-like -system. When this method is called 3x, it abruptly exits the application -(aka emergency abort). - -The parameter `exit_code` allows you to specify the application exit -code. +### Run-time -!!! note - You need to install :py`win32api` - module to use `Ctrl-C` or an emergency abord properly with ASAB on - Windows. It is an optional dependency of ASAB. +The *run-time* starts after all the modules and services are loaded. This is where the application typically spends the most time. +At this time: -### Exit-time +- [Publish-Subscribe](/reference/pubsub/reference/#well-known-messages) message **Application.run!** is published. +- The asynchronous callback `Application.main()` is executed. -The application object executes asynchronous callback -`Application.finalize()`, which can be overridden by an user. +The coroutine `Application.main()` is intended to be overwritten by an user. +If `main()` method is completed without calling `stop()`, then the application will run forever. ``` python class MyApplication(asab.Application): - async def finalize(self): - # Custom finalization - ... + async def main(self): + print("Hello world!") + self.stop() ``` -The Publish-Subscribe message `Application.exit!` is published when exit-time begins. - -Set the exit code of the application, see `os.exit()` in the Python -documentation. If `force` is `False`, the exit code will be set only if -the previous value is lower than the new one. If `force` is `True`, the -exit code value is set to a `exit_code` disregarding the previous value. - -The actual value of the exit code. +### Exit-time -The example of the exit code handling in the `main()` function of the -application. +The method `Application.stop()` gracefully terminates the *run-time* and commences the *exit-time*. +This method is automatically called by `SIGINT` and `SIGTERM`. +It also includes a response to `Ctrl-C` on UNIX-like system. +When this method is called *exactly three times*, it abruptly exits the application (aka emergency abort). -```python -if __name__ == '__main__': - app = asab.Application() - exit_code = app.run() - sys.exit(exit_code) -``` +!!! note + You need to install `win32api` module to use `Ctrl-C` or an emergency abort properly with ASAB on Windows. + It is an optional dependency of ASAB. -## Registering modules and services +The parameter `exit_code` allows you to specify the application exit code. +At *exit-time*: -For more details see `Module` class. +- [Publish-Subscribe](/reference/pubsub/reference/#well-known-messages) message **Application.exit!** is published. +- Asynchronous callback `Application.finalize()` is executed. -Initialize and add a new module. The `module_class` class will be -instantiated during the method call. +`Application.finalize()` is intended to be overridden by an user. +It can be used for storing backup data for the next start of the application, custom operations when terminating services, sending signals to other applications etc. ``` python class MyApplication(asab.Application): - async def initialize(self): - from my_module import MyModule - self.add_module(MyModule) -``` - -A list of modules that has been added to the application. - - -Each service is identified by its unique service name. For more details -see `Service` class. - -Locate a service by its service name in a registry and return the -`Service` object. - -``` python -svc = app.get_service("service_sample") -svc.hello() + async def finalize(self): + # Custom finalization + ... ``` -A dictionary of registered services. ## Command-line parser From 1e26f516e8816c5f180a94c842ca9e99daa8afcc Mon Sep 17 00:00:00 2001 From: mejroslav Date: Thu, 10 Aug 2023 13:02:38 +0200 Subject: [PATCH 4/5] Add Registering modules and services section --- docs/reference/modules_services/reference.md | 24 +++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/docs/reference/modules_services/reference.md b/docs/reference/modules_services/reference.md index 21afd29c3..6ffdf2627 100644 --- a/docs/reference/modules_services/reference.md +++ b/docs/reference/modules_services/reference.md @@ -2,7 +2,29 @@ ASAB applications contain several **Services**. Each Service is located in a separate **Module**. -## Built-in Services and Modules: + +## Registering Modules and Services + +The method [`asab.Application.add_module()`](/reference/application/reference/#asab.Application.add_module) initializes and adds a new module. +The `module_class` class will be instantiated during the method call. +Modules that has been added to the application are stored in [`asab.Application.Modules`](/reference/application/reference/#asab.application.Application.Modules) list. + +``` python +class MyApplication(asab.Application): + async def initialize(self): + from my_module import MyModule + self.add_module(MyModule) +``` + +The method [`asab.Application.add_service()`](#asab.Application.add_service) locates a service by its service name +in a registry [`Services`](/reference/application/reference/#asab.Application.Services) and returns the [`asab.Service`](#asab.Service) object. + +``` python +svc = app.get_service("service_sample") +svc.hello() +``` + +## Built-in Services Table of ASAB built-in Services and Modules: From 4094f059332b21556bfe851a43f124c2fc5975b1 Mon Sep 17 00:00:00 2001 From: mejroslav Date: Thu, 10 Aug 2023 13:18:38 +0200 Subject: [PATCH 5/5] Simplify --- docs/reference/modules_services/reference.md | 36 +++++++++++--------- 1 file changed, 19 insertions(+), 17 deletions(-) diff --git a/docs/reference/modules_services/reference.md b/docs/reference/modules_services/reference.md index 6ffdf2627..f799645f4 100644 --- a/docs/reference/modules_services/reference.md +++ b/docs/reference/modules_services/reference.md @@ -5,40 +5,42 @@ ASAB applications contain several **Services**. Each Service is located in a sep ## Registering Modules and Services -The method [`asab.Application.add_module()`](/reference/application/reference/#asab.Application.add_module) initializes and adds a new module. -The `module_class` class will be instantiated during the method call. -Modules that has been added to the application are stored in [`asab.Application.Modules`](/reference/application/reference/#asab.application.Application.Modules) list. - ``` python class MyApplication(asab.Application): async def initialize(self): from my_module import MyModule - self.add_module(MyModule) -``` + self.add_module(MyModule) #(1)! + self.MyService = self.get_service("MyService") #(2)! + ... -The method [`asab.Application.add_service()`](#asab.Application.add_service) locates a service by its service name -in a registry [`Services`](/reference/application/reference/#asab.Application.Services) and returns the [`asab.Service`](#asab.Service) object. +# ...somewhere in the code -``` python -svc = app.get_service("service_sample") -svc.hello() +def custom_function(): + my_service = app.Services.get("MyService") #(3)! + my_service.do_stuff() ``` +1. The method [`add_module()`](/reference/application/reference/#asab.Application.add_module) initializes and adds a new module. +The `module_class` class will be instantiated during the method call. +2. The method [`get_service()`](#asab.Application.get_service) locates a service by its name and returns the [`asab.Service`](#asab.Service) object. +3. Modules that has been added to the application are stored in [`asab.Application.Modules`](/reference/application/reference/#asab.application.Application.Modules) list. Similarly, Services are stored in [`asab.Application.Services`](/reference/application/reference/#asab.Application.Services) dictionary. + + ## Built-in Services Table of ASAB built-in Services and Modules: | Service | Module | Features | | --- | --- | --- | -| `WebService` | `asab.web` | Creating a web server. | -| `StorageService` | `asab.storage` | Storing the data in various databases. | +| [`WebService`](/reference/web/web-server) | `asab.web` | Creating a web server. | +| [`StorageService`](/reference/storage/reference) | `asab.storage` | Storing the data in various databases. | | [`LibraryService`](/reference/library/reference) | `asab.library` | Reading the data from various sources. | -| `ZooKeeperService` | `asab.zookeeper` | Synchronizing data with Apache Zookeeper. | -| [`MetricService`](/reference/metrics/service/) | `asab.metric` | Analysis of the application state in a timescale manner.| -| `AlertService`| `asab.alert` | Integration of Alert Managers. | +| [`ZooKeeperService`](/reference/zookeeper/reference) | `asab.zookeeper` | Synchronizing data with Apache Zookeeper. | +| [`MetricService`](/reference/metrics/reference) | `asab.metric` | Analysis of the application state in a timescale manner.| +| [`AlertService`](/reference/alert/reference) | `asab.alert` | Integration of Alert Managers. | | `TaskService`| `asab.task`| Execution of one-off background tasks. | | `ProactorService` | `asab.proactor` | Running long-time activities asynchronously. | -| `ApiService`| `asab.api` | Implementation of Swagger documentation. | +| [`ApiService`](/reference/web/rest*_api_docs) | `asab.api` | Implementation of Swagger documentation. | ::: asab.Module