pavao 0.10.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (68) hide show
  1. pavao-0.10.1/LICENSE +21 -0
  2. pavao-0.10.1/PKG-INFO +159 -0
  3. pavao-0.10.1/README.md +125 -0
  4. pavao-0.10.1/pavao/__init__.py +25 -0
  5. pavao-0.10.1/pavao/__main__.py +5 -0
  6. pavao-0.10.1/pavao/broadcasting.py +59 -0
  7. pavao-0.10.1/pavao/builtin/foundation/sqlite/persisted_settings.py +51 -0
  8. pavao-0.10.1/pavao/builtin/foundation/sqlite/supplier.py +103 -0
  9. pavao-0.10.1/pavao/builtin/foundation/sqlite/task_store.py +201 -0
  10. pavao-0.10.1/pavao/config.py +171 -0
  11. pavao-0.10.1/pavao/daemon.py +128 -0
  12. pavao-0.10.1/pavao/endpoint.py +6 -0
  13. pavao-0.10.1/pavao/exception.py +4 -0
  14. pavao-0.10.1/pavao/filter.py +75 -0
  15. pavao-0.10.1/pavao/http/__init__.py +6 -0
  16. pavao-0.10.1/pavao/http/cookie.py +172 -0
  17. pavao-0.10.1/pavao/http/handler.py +200 -0
  18. pavao-0.10.1/pavao/http/request.py +174 -0
  19. pavao-0.10.1/pavao/http/response.py +90 -0
  20. pavao-0.10.1/pavao/i18n.py +154 -0
  21. pavao-0.10.1/pavao/json.py +322 -0
  22. pavao-0.10.1/pavao/lifespan/__init__.py +4 -0
  23. pavao-0.10.1/pavao/lifespan/handler.py +56 -0
  24. pavao-0.10.1/pavao/loader.py +243 -0
  25. pavao-0.10.1/pavao/logger.py +102 -0
  26. pavao-0.10.1/pavao/parameters.py +135 -0
  27. pavao-0.10.1/pavao/persistence.py +77 -0
  28. pavao-0.10.1/pavao/pkg.py +381 -0
  29. pavao-0.10.1/pavao/predicates.py +10 -0
  30. pavao-0.10.1/pavao/refresher.py +225 -0
  31. pavao-0.10.1/pavao/registry.py +263 -0
  32. pavao-0.10.1/pavao/run.py +70 -0
  33. pavao-0.10.1/pavao/scheduler.py +190 -0
  34. pavao-0.10.1/pavao/server.py +485 -0
  35. pavao-0.10.1/pavao/sse.py +183 -0
  36. pavao-0.10.1/pavao/supplier.py +274 -0
  37. pavao-0.10.1/pavao/task.py +245 -0
  38. pavao-0.10.1/pavao/utils.py +134 -0
  39. pavao-0.10.1/pavao.egg-info/PKG-INFO +159 -0
  40. pavao-0.10.1/pavao.egg-info/SOURCES.txt +66 -0
  41. pavao-0.10.1/pavao.egg-info/dependency_links.txt +1 -0
  42. pavao-0.10.1/pavao.egg-info/entry_points.txt +3 -0
  43. pavao-0.10.1/pavao.egg-info/requires.txt +13 -0
  44. pavao-0.10.1/pavao.egg-info/top_level.txt +1 -0
  45. pavao-0.10.1/pyproject.toml +61 -0
  46. pavao-0.10.1/setup.cfg +4 -0
  47. pavao-0.10.1/tests/test_broadcasting.py +113 -0
  48. pavao-0.10.1/tests/test_config.py +260 -0
  49. pavao-0.10.1/tests/test_cookie.py +134 -0
  50. pavao-0.10.1/tests/test_daemon.py +353 -0
  51. pavao-0.10.1/tests/test_filter.py +179 -0
  52. pavao-0.10.1/tests/test_handler.py +493 -0
  53. pavao-0.10.1/tests/test_i18n.py +284 -0
  54. pavao-0.10.1/tests/test_json.py +486 -0
  55. pavao-0.10.1/tests/test_lifespan.py +200 -0
  56. pavao-0.10.1/tests/test_loader.py +380 -0
  57. pavao-0.10.1/tests/test_logger.py +138 -0
  58. pavao-0.10.1/tests/test_parameters.py +210 -0
  59. pavao-0.10.1/tests/test_refresher.py +531 -0
  60. pavao-0.10.1/tests/test_registry.py +323 -0
  61. pavao-0.10.1/tests/test_request.py +288 -0
  62. pavao-0.10.1/tests/test_response.py +183 -0
  63. pavao-0.10.1/tests/test_scheduler.py +537 -0
  64. pavao-0.10.1/tests/test_server.py +1067 -0
  65. pavao-0.10.1/tests/test_sse.py +184 -0
  66. pavao-0.10.1/tests/test_supplier.py +411 -0
  67. pavao-0.10.1/tests/test_task.py +450 -0
  68. pavao-0.10.1/tests/test_utils.py +166 -0
pavao-0.10.1/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2023 Gustavo Venturini
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
pavao-0.10.1/PKG-INFO ADDED
@@ -0,0 +1,159 @@
1
+ Metadata-Version: 2.4
2
+ Name: pavao
3
+ Version: 0.10.1
4
+ Summary: Modular, injection based, Python ASGI web framework
5
+ Author-email: Gustavo Venturini <gustavo.c.venturini@gmail.com>
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/vottini/pavao
8
+ Keywords: framework,asgi,api,web,rest,http,dependency-injection,server-sent-events
9
+ Classifier: Development Status :: 4 - Beta
10
+ Classifier: Intended Audience :: Developers
11
+ Classifier: Operating System :: OS Independent
12
+ Classifier: Programming Language :: Python :: 3 :: Only
13
+ Classifier: Topic :: Internet :: WWW/HTTP :: HTTP Servers
14
+ Classifier: Topic :: Internet :: WWW/HTTP :: WSGI :: Application
15
+ Classifier: Topic :: Software Development :: Libraries :: Application Frameworks
16
+ Classifier: Framework :: AsyncIO
17
+ Requires-Python: >=3.11
18
+ Description-Content-Type: text/markdown
19
+ License-File: LICENSE
20
+ Requires-Dist: aiosqlite==0.20.0
21
+ Requires-Dist: click==8.1.7
22
+ Requires-Dist: croniter==3.0.3
23
+ Requires-Dist: h11==0.14.0
24
+ Requires-Dist: python-dateutil==2.9.0.post0
25
+ Requires-Dist: pytz==2024.2
26
+ Requires-Dist: PyYAML==6.0.2
27
+ Requires-Dist: six==1.16.0
28
+ Requires-Dist: typing_extensions==4.12.2
29
+ Requires-Dist: uvicorn==0.30.6
30
+ Requires-Dist: watchdog==4.0.2
31
+ Requires-Dist: jsonbp==1.3.1
32
+ Requires-Dist: umapper==0.1.6
33
+ Dynamic: license-file
34
+
35
+
36
+ # pavao
37
+
38
+ [![Documentation Status](https://readthedocs.org/projects/pavao/badge/?version=latest)](http://pavao.readthedocs.io/?badge=latest)
39
+ [![codecov](https://codecov.io/github/vottini/pavao/graph/badge.svg?token=N2H2WJ0SC5)](https://codecov.io/github/vottini/pavao)
40
+
41
+ **pavao** (from "Pavão", Portuguese for *peacock*) is a Python ASGI framework built
42
+ on two ideas that work together.
43
+
44
+ One is **modularity**: point pavao at a directory of Python files and it discovers and
45
+ serves every endpoint module it finds there. Adding a new endpoint means dropping a file
46
+ into the directory; removing one means deleting it. Nothing else in the application needs
47
+ to change.
48
+
49
+ The other is **name-based dependency injection**: instead of importing or constructing the
50
+ objects your endpoint needs, you just declare them as arguments to your handler function.
51
+ pavao uses the names of the parameters to deliver the correct objects where they are requested.
52
+ Parameters are instantiated on demand — if a module does not declare `db_connection`,
53
+ no database connection is created for it.
54
+
55
+ A minimal endpoint:
56
+
57
+ ```python
58
+ def process(response):
59
+ response.set_plain_text("World")
60
+
61
+ def pavao_endpoint():
62
+ return {
63
+ 'method': 'GET',
64
+ 'path': '/hello',
65
+ 'processor': process
66
+ }
67
+ ```
68
+
69
+ ## Quick start
70
+
71
+ ```console
72
+ $ python -m venv .venv
73
+ $ source .venv/bin/activate
74
+ $ pip install pavao
75
+ $ mkdir modules
76
+ ```
77
+
78
+ Save the example above as `modules/hello.py`, then:
79
+
80
+ ```console
81
+ $ pavao-run modules/
82
+ ```
83
+
84
+ Open `http://localhost:8000/hello` and you should see `World`.
85
+
86
+ ## Going further
87
+
88
+ Both pillars extend naturally as an application grows.
89
+
90
+ On the modularity side, modules can be organised into subdirectories, share helper code through plain Python imports, and be hot-reloaded on change without restarting the server. Because each endpoint lives in its own file, the reload boundary is exactly the unit of work: edit one handler, save, and only that module is reloaded — the rest of the application keeps running. Alongside endpoint modules, the same directory can contain scheduled task modules (`pavao_task()`) and always-on background service modules (`pavao_service()`).
91
+
92
+ On the injection side, the built-in injectables — `request`, `response`, `path_variables`, `broadcasting`, and others — cover the common cases. The set is open: custom suppliers placed in a foundation directory can introduce new named parameters, making application-specific resources (database connections, caches, auth tokens) available to any module that declares them.
93
+
94
+ Crucially, injection applies to descriptor functions too, not only to processors. A module can declare a dependency in its `pavao_endpoint()` function and use it to decide whether to register at all, or to configure the endpoint based on runtime state. Each function asks for exactly what it needs — nothing is shared implicitly.
95
+
96
+ ```python
97
+ import pavao
98
+
99
+ delete_refresh = pavao.include('queries/delete_refresh.sql')
100
+
101
+ async def process(response, db_connection, auth_data):
102
+ row = await db_connection.fetchrow(
103
+ "SELECT id FROM pavao.refresh_token WHERE account = $1",
104
+ auth_data.account_id)
105
+
106
+ if row is None:
107
+ response.set_status(404)
108
+ return
109
+
110
+ await db_connection.execute(
111
+ delete_refresh,
112
+ auth_data.account_id,
113
+ auth_data.token)
114
+
115
+ response.add_cookie(
116
+ pavao.http.Cookie('token')
117
+ .set_path('/refresh-token')
118
+ .set_sameSite(pavao.http.SameSite.STRICT)
119
+ .set_to_be_removed()
120
+ )
121
+
122
+ response.set_status(204)
123
+
124
+
125
+ def pavao_endpoint(loaded_settings):
126
+ if not loaded_settings.get_setting('api.v1.enabled', True):
127
+ return
128
+
129
+ return {
130
+ 'method': 'DELETE',
131
+ 'path': '/v1/logout',
132
+ 'processor': process
133
+ }
134
+ ```
135
+
136
+ The descriptor receives `loaded_settings` and checks a configuration flag — if the v1 API is disabled the module is quietly skipped. Otherwise the endpoint is registered, and its processor independently declares `response`, `db_connection`, and `auth_data`. Each function asks for exactly what it needs at the time it needs it.
137
+
138
+ ## Features
139
+
140
+ - Point-and-serve module directory: endpoints, tasks, and services auto-discovered at startup
141
+ - Dependency injection by parameter name across all modules
142
+ - Typed URL path variables and URL query parameters
143
+ - Schema-based YAML configuration with environment variable substitution
144
+ - Endpoint access filtering with full DI support
145
+ - Asynchronous inter-module communication bus
146
+ - Scheduled tasks (cron-style) and always-on background services
147
+ - Typed JSON parsing and serialization via [jsonbp](https://github.com/vottini/jsonbp)
148
+ - Server-Sent Events (SSE) with heartbeat and disconnection support
149
+ - Hot-reloading of modules on file change
150
+ - Response internationalization
151
+ - Extension management via `pavao-pkg`
152
+
153
+ ## Documentation
154
+
155
+ Full documentation is available at [vottini.github.io/pavao](https://vottini.github.io/pavao).
156
+
157
+ ## License
158
+
159
+ MIT
pavao-0.10.1/README.md ADDED
@@ -0,0 +1,125 @@
1
+
2
+ # pavao
3
+
4
+ [![Documentation Status](https://readthedocs.org/projects/pavao/badge/?version=latest)](http://pavao.readthedocs.io/?badge=latest)
5
+ [![codecov](https://codecov.io/github/vottini/pavao/graph/badge.svg?token=N2H2WJ0SC5)](https://codecov.io/github/vottini/pavao)
6
+
7
+ **pavao** (from "Pavão", Portuguese for *peacock*) is a Python ASGI framework built
8
+ on two ideas that work together.
9
+
10
+ One is **modularity**: point pavao at a directory of Python files and it discovers and
11
+ serves every endpoint module it finds there. Adding a new endpoint means dropping a file
12
+ into the directory; removing one means deleting it. Nothing else in the application needs
13
+ to change.
14
+
15
+ The other is **name-based dependency injection**: instead of importing or constructing the
16
+ objects your endpoint needs, you just declare them as arguments to your handler function.
17
+ pavao uses the names of the parameters to deliver the correct objects where they are requested.
18
+ Parameters are instantiated on demand — if a module does not declare `db_connection`,
19
+ no database connection is created for it.
20
+
21
+ A minimal endpoint:
22
+
23
+ ```python
24
+ def process(response):
25
+ response.set_plain_text("World")
26
+
27
+ def pavao_endpoint():
28
+ return {
29
+ 'method': 'GET',
30
+ 'path': '/hello',
31
+ 'processor': process
32
+ }
33
+ ```
34
+
35
+ ## Quick start
36
+
37
+ ```console
38
+ $ python -m venv .venv
39
+ $ source .venv/bin/activate
40
+ $ pip install pavao
41
+ $ mkdir modules
42
+ ```
43
+
44
+ Save the example above as `modules/hello.py`, then:
45
+
46
+ ```console
47
+ $ pavao-run modules/
48
+ ```
49
+
50
+ Open `http://localhost:8000/hello` and you should see `World`.
51
+
52
+ ## Going further
53
+
54
+ Both pillars extend naturally as an application grows.
55
+
56
+ On the modularity side, modules can be organised into subdirectories, share helper code through plain Python imports, and be hot-reloaded on change without restarting the server. Because each endpoint lives in its own file, the reload boundary is exactly the unit of work: edit one handler, save, and only that module is reloaded — the rest of the application keeps running. Alongside endpoint modules, the same directory can contain scheduled task modules (`pavao_task()`) and always-on background service modules (`pavao_service()`).
57
+
58
+ On the injection side, the built-in injectables — `request`, `response`, `path_variables`, `broadcasting`, and others — cover the common cases. The set is open: custom suppliers placed in a foundation directory can introduce new named parameters, making application-specific resources (database connections, caches, auth tokens) available to any module that declares them.
59
+
60
+ Crucially, injection applies to descriptor functions too, not only to processors. A module can declare a dependency in its `pavao_endpoint()` function and use it to decide whether to register at all, or to configure the endpoint based on runtime state. Each function asks for exactly what it needs — nothing is shared implicitly.
61
+
62
+ ```python
63
+ import pavao
64
+
65
+ delete_refresh = pavao.include('queries/delete_refresh.sql')
66
+
67
+ async def process(response, db_connection, auth_data):
68
+ row = await db_connection.fetchrow(
69
+ "SELECT id FROM pavao.refresh_token WHERE account = $1",
70
+ auth_data.account_id)
71
+
72
+ if row is None:
73
+ response.set_status(404)
74
+ return
75
+
76
+ await db_connection.execute(
77
+ delete_refresh,
78
+ auth_data.account_id,
79
+ auth_data.token)
80
+
81
+ response.add_cookie(
82
+ pavao.http.Cookie('token')
83
+ .set_path('/refresh-token')
84
+ .set_sameSite(pavao.http.SameSite.STRICT)
85
+ .set_to_be_removed()
86
+ )
87
+
88
+ response.set_status(204)
89
+
90
+
91
+ def pavao_endpoint(loaded_settings):
92
+ if not loaded_settings.get_setting('api.v1.enabled', True):
93
+ return
94
+
95
+ return {
96
+ 'method': 'DELETE',
97
+ 'path': '/v1/logout',
98
+ 'processor': process
99
+ }
100
+ ```
101
+
102
+ The descriptor receives `loaded_settings` and checks a configuration flag — if the v1 API is disabled the module is quietly skipped. Otherwise the endpoint is registered, and its processor independently declares `response`, `db_connection`, and `auth_data`. Each function asks for exactly what it needs at the time it needs it.
103
+
104
+ ## Features
105
+
106
+ - Point-and-serve module directory: endpoints, tasks, and services auto-discovered at startup
107
+ - Dependency injection by parameter name across all modules
108
+ - Typed URL path variables and URL query parameters
109
+ - Schema-based YAML configuration with environment variable substitution
110
+ - Endpoint access filtering with full DI support
111
+ - Asynchronous inter-module communication bus
112
+ - Scheduled tasks (cron-style) and always-on background services
113
+ - Typed JSON parsing and serialization via [jsonbp](https://github.com/vottini/jsonbp)
114
+ - Server-Sent Events (SSE) with heartbeat and disconnection support
115
+ - Hot-reloading of modules on file change
116
+ - Response internationalization
117
+ - Extension management via `pavao-pkg`
118
+
119
+ ## Documentation
120
+
121
+ Full documentation is available at [vottini.github.io/pavao](https://vottini.github.io/pavao).
122
+
123
+ ## License
124
+
125
+ MIT
@@ -0,0 +1,25 @@
1
+
2
+ from .exception import PavaoException
3
+ from .logger import Logger
4
+ from .utils import adapter, async_adapter, silent_generator
5
+ from .config import ConfigException, load_settings
6
+ from .filter import RequestFilter
7
+ from .supplier import ParameterSupplier
8
+ from .persistence import TaskStore, StoredSettings
9
+ from .i18n import Internationalization, ModuleLocalizer, LazyLocalizer
10
+ from .json import accepts_json, generates_json, error_blueprint, field_present
11
+ from .sse import generates_sse
12
+ from .parameters import accepts_parameters
13
+ from .loader import path_to, include, import_module, from_module
14
+ from .broadcasting import SHUTDOWN_TOPIC
15
+ from .scheduler import TaskProblem
16
+ from .server import Server
17
+
18
+ from umapper import register_mapping_class
19
+ from umapper import assemble_dicts
20
+ from umapper import convert_to_object
21
+
22
+ from . import predicates
23
+ from . import lifespan
24
+ from . import http
25
+
@@ -0,0 +1,5 @@
1
+
2
+ from .run import main
3
+
4
+ if __name__ == "__main__":
5
+ main()
@@ -0,0 +1,59 @@
1
+
2
+ import asyncio
3
+
4
+ SHUTDOWN_TOPIC = "PAVAO.SHUTDOWN"
5
+
6
+ class Sink:
7
+ def __init__(self, topic):
8
+ # maxsize=0 means unbounded; set a positive value to apply backpressure
9
+ self.queue = asyncio.Queue()
10
+ self.topic = topic
11
+
12
+ async def get_message(self):
13
+ msg = await self.queue.get()
14
+ self.queue.task_done()
15
+ return msg
16
+
17
+
18
+ class Broadcaster:
19
+ def __init__(self):
20
+ self.topics = dict()
21
+
22
+ def open_sink(self, topic):
23
+ sink = Sink(topic)
24
+ topic_sinks = self.topics.get(topic, list())
25
+ topic_sinks.append(sink)
26
+ self.topics[topic] = topic_sinks
27
+ return sink
28
+
29
+
30
+ async def write_message(self, topic, message):
31
+ if not topic in self.topics: return
32
+ for sink in self.topics[topic]:
33
+ await sink.queue.put(message)
34
+
35
+
36
+ def close_sink(self, sink):
37
+ topic_sinks = self.topics.get(sink.topic, list())
38
+
39
+ filtered_sinks = [ snk
40
+ for snk in topic_sinks
41
+ if snk is not sink
42
+ ]
43
+
44
+ if len(filtered_sinks) == 0:
45
+ self.topics.pop(sink.topic, None)
46
+
47
+ else:
48
+ self.topics[sink.topic] = filtered_sinks
49
+
50
+
51
+ async def announce_shutdown(self):
52
+ await self.write_message(SHUTDOWN_TOPIC, None)
53
+
54
+
55
+ __all__ = [
56
+ "SHUTDOWN_TOPIC",
57
+ "Broadcaster",
58
+ ]
59
+
@@ -0,0 +1,51 @@
1
+
2
+ import re
3
+ import asyncio
4
+ import builtins
5
+ import collections
6
+ import pavao
7
+
8
+ from pavao.persistence import StoredSettings
9
+
10
+ get_setting = pavao.include("queries/get_setting.sql")
11
+ upsert_setting = pavao.include("queries/upsert_setting.sql")
12
+ list_pattern = re.compile(r"\[([^\]]+)\]")
13
+
14
+ class PersistedSettings(StoredSettings):
15
+ def __init__(self, internal_db):
16
+ self.internal_db = internal_db
17
+
18
+
19
+ async def get(self, setting):
20
+ async with self.internal_db.execute(get_setting, [setting]) as cursor:
21
+ row = await cursor.fetchone()
22
+ if row is None:
23
+ return None
24
+
25
+ typ, value = row
26
+ list_search = list_pattern.search(value)
27
+ ctor = getattr(builtins, typ)
28
+
29
+ if list_search is not None:
30
+ list_content = list_search.group(1)
31
+ return [ ctor(vle) for vle in list_content.split("::") ]
32
+
33
+ return ctor(value)
34
+
35
+
36
+ async def set(self, setting, value):
37
+ value_type = type(value).__name__
38
+ original_value = value
39
+
40
+ if isinstance(value, collections.abc.Sequence) and not isinstance(value, str):
41
+ value_type = type(value[0]).__name__
42
+ str_values = map(str, value)
43
+ value = f"[{'::'.join(str_values)}]"
44
+
45
+ async with self.internal_db.cursor() as cursor:
46
+ await cursor.execute(upsert_setting, {
47
+ 'name': setting,
48
+ 'type': value_type,
49
+ 'value': str(value)
50
+ })
51
+
@@ -0,0 +1,103 @@
1
+
2
+ import os
3
+ import asyncio
4
+ import datetime
5
+ import aiosqlite
6
+ import sqlite3
7
+ import umapper
8
+ import pavao
9
+
10
+ from pavao.builtin.foundation.sqlite.task_store import SqliteTaskStore
11
+
12
+ config_schema = {
13
+ "enabled": True,
14
+ "persist": True,
15
+ "filename": "pavao.db",
16
+ "trace": False
17
+ }
18
+
19
+ queries_dir = pavao.path_to('database')
20
+ db_creation = [arq for arq in os.listdir(queries_dir) if arq.endswith('.sql')]
21
+ db_creation.sort()
22
+
23
+ #-------------------------------------------------------------------------------
24
+
25
+ def convert_datetime(val):
26
+ return datetime.datetime.fromisoformat(
27
+ val.decode())
28
+
29
+ aiosqlite.register_adapter(datetime.datetime, lambda val: val.isoformat())
30
+ aiosqlite.register_adapter(bool, lambda val: '1' if val else '0')
31
+ aiosqlite.register_converter('timestamp', convert_datetime)
32
+ aiosqlite.register_converter('bool', lambda val: val.decode() == '1')
33
+
34
+ #-------------------------------------------------------------------------------
35
+
36
+ pavao.from_module('persisted_settings').import_item('PersistedSettings')
37
+
38
+ class SqliteSupplier(pavao.ParameterSupplier):
39
+ def __init__(self):
40
+ self.connection = None
41
+ self.specs = None
42
+
43
+
44
+ def parameters_supplied(self):
45
+ if not self.specs.enabled:
46
+ return dict()
47
+
48
+ async def provide_task_store():
49
+ return SqliteTaskStore(self.connection)
50
+
51
+ async def provide_stored_settings():
52
+ return PersistedSettings(self.connection)
53
+
54
+ return {
55
+ 'task_store': [provide_task_store, None],
56
+ 'stored_settings': [provide_stored_settings, None]
57
+ }
58
+
59
+
60
+ def configure(self, loaded_settings):
61
+ db_settings = loaded_settings.get_setting(
62
+ "pavao.database.sqlite",
63
+ config_schema)
64
+
65
+ self.specs = umapper.convert_to_object(db_settings)
66
+
67
+
68
+ async def initialize(self):
69
+ if not self.specs.enabled:
70
+ return
71
+
72
+ try:
73
+ if not self.specs.persist:
74
+ pavao.Logger.info("Pavao internal database set as not persistent")
75
+ db_name = ':memory:'
76
+
77
+ else: db_name = self.specs.filename
78
+ self.connection = await aiosqlite.connect(db_name,
79
+ detect_types=sqlite3.PARSE_DECLTYPES)
80
+
81
+ if self.specs.trace:
82
+ sqlite3.enable_callback_tracebacks(True)
83
+ await self.connection.set_trace_callback(print)
84
+
85
+ pavao.Logger.info("Pavao internal database connected")
86
+
87
+ for query in db_creation:
88
+ async with self.connection.cursor() as cursor:
89
+ with open(os.path.join(queries_dir, query)) as fd:
90
+ await cursor.executescript(fd.read())
91
+
92
+ except Exception as e:
93
+ pavao.Logger.error("Error while setting up internal database: " + str(e))
94
+ raise pavao.PavaoException("Unable to setup internal Pavao database")
95
+
96
+ #------------------------------------------------------------
97
+
98
+ async def shutdown(self):
99
+ if self.connection is not None:
100
+ await self.connection.close()
101
+ pavao.Logger.info("Pavao internal database connection closed")
102
+ self.connection = None
103
+