Skip to content

Commit f896de6

Browse files
committed
Move logger plugin to client monorepo
1 parent 1dfd3cf commit f896de6

21 files changed

+4460
-1
lines changed

.gitignore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ profile_default/
8282
ipython_config.py
8383

8484
# pyenv
85-
.python-version
85+
# .python-version
8686

8787
# pipenv
8888
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.

.python-version

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
3.10
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
__pycache__
2+
.idea
3+
.vscode
4+
dist
5+
**/.pytest_cache/
6+
**/node_modules/
7+
**/.tox/
8+
wrap/
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
# Logger Python Plugin
2+
The Logger plugin implements the `logger-interface` from the `ens/wraps.eth:[email protected]` package (see [./src/schema.graphql](./src/schema.graphql)). By default, it logs all events using the Python `logging` module. You can customize this behavior by setting the `Logger` property on the plugin's configuration object (examples below).
3+
4+
## Usage
5+
### 1. Configure Client
6+
When creating your Polywrap Python client, add the logger plugin:
7+
```python
8+
from polywrap_client_config_builder import PolywrapClientConfigBuilder
9+
from polywrap_logger_plugin import logger_plugin
10+
from polywrap_client import PolywrapClient
11+
12+
config = PolywrapClientConfigBuilder().set_package(
13+
uri=Uri.from_str("plugin/logger"),
14+
package=logger_plugin()
15+
).set_interface(
16+
interface=Uri.from_str("ens/wraps.eth:[email protected]"),
17+
implementations=[Uri.from_str("plugin/logger")]
18+
).set_redirect(
19+
Uri.from_str("ens/wraps.eth:[email protected]"),
20+
Uri.from_str("plugin/logger")
21+
).build()
22+
client = PolywrapClient(config)
23+
```
24+
25+
### 2. Invoke The Logger
26+
Invocations to the logger plugin can be made via the interface URI (which will get redirected), or the plugin's URI directly:
27+
```python
28+
await client.invoke({
29+
'uri': 'ens/wraps.eth:[email protected]' | 'plugin/logger',
30+
'method': 'log',
31+
'args': {
32+
'level': 'INFO',
33+
'message': 'foo bar baz'
34+
}
35+
})
36+
```
37+
38+
### 3. Customize The Logger
39+
When adding the logger to your client, you can add your own custom log function:
40+
```python
41+
config = PolywrapClientConfigBuilder().set_package(
42+
uri=Uri.from_str("plugin/logger"),
43+
package=logger_plugin(LoggerConfig(logger=YourLogger(), level=LogLevel.INFO))
44+
).set_interface(
45+
interface=Uri.from_str("ens/wraps.eth:[email protected]"),
46+
implementations=[Uri.from_str("plugin/logger")]
47+
).set_redirect(
48+
Uri.from_str("ens/wraps.eth:[email protected]"),
49+
Uri.from_str("plugin/logger")
50+
).build()
51+
```
52+
53+
### 4. Add Multiple Loggers
54+
Multiple logger implementations can be added to the client:
55+
```python
56+
config = PolywrapClientConfigBuilder().set_package(
57+
uri=Uri.from_str("plugin/logger"),
58+
package=logger_plugin(LoggerConfig(logger=YourLogger(), level=LogLevel.INFO))
59+
).set_interface(
60+
interface=Uri.from_str("ens/wraps.eth:[email protected]"),
61+
implementations=[Uri.from_str("plugin/logger"), Uri.from_str("plugin/custom-logger")]
62+
).set_package(
63+
uri=Uri.from_str("plugin/custom-logger"),
64+
package=custom_logger_plugin()
65+
).build()
66+
```
67+
68+
### 5. Invoke All Logger Implementations
69+
When you'd like to log something to more than one logger, you can invoke all implementations of the logger interface:
70+
```python
71+
implementations = client.get_implementations('ens/wraps.eth:[email protected]')
72+
73+
for impl in implementations:
74+
await client.invoke({
75+
'uri': impl,
76+
'method': 'log',
77+
'args': {
78+
'level': 'INFO',
79+
'message': 'message'
80+
}
81+
})
82+
```
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
{
2+
"name": "@polywrap/python-logger-plugin",
3+
"description": "Polywrap Python Logger Plugin",
4+
"version": "0.10.1",
5+
"private": true,
6+
"devDependencies": {
7+
"polywrap": "0.10.2"
8+
},
9+
"scripts": {
10+
"codegen": "npx polywrap codegen -g polywrap_logger_plugin/wrap"
11+
}
12+
}

packages/plugins/polywrap-logger-plugin/poetry.lock

Lines changed: 1153 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
format: 0.3.0
2+
project:
3+
name: Logger
4+
type: plugin/python
5+
source:
6+
schema: ./schema.graphql
7+
module: ./polywrap_logger_plugin/__init__.py
8+
import_abis:
9+
- uri: wrap://ens/wraps.eth:[email protected]
10+
abi: ../../interface/polywrap.graphql
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
"""This package contains the Logger plugin."""
2+
import logging
3+
from dataclasses import dataclass, field
4+
5+
from polywrap_plugin import PluginPackage
6+
7+
from .wrap import ArgsLog, InvokerClient, LogLevel, Module, manifest
8+
9+
10+
@dataclass
11+
class LoggerConfig:
12+
"""Defines the configuration for the Logger plugin."""
13+
14+
logger: logging.Logger = field(default_factory=logging.getLogger)
15+
level: LogLevel = LogLevel.INFO
16+
17+
18+
class LoggerModule(Module[LoggerConfig]):
19+
"""Defines the Logger module."""
20+
21+
def __init__(self, config: LoggerConfig):
22+
"""Initializes the Logger module."""
23+
self.config = config
24+
self.logger = self.config.logger
25+
self.logger.setLevel(self.config.level.name)
26+
27+
def log(self, args: ArgsLog, client: InvokerClient, env: None) -> bool:
28+
"""Logs a message."""
29+
level = (args["level"].value + 1) * 10
30+
self.logger.log(level, args["message"])
31+
return True
32+
33+
34+
def logger_plugin(config: LoggerConfig = LoggerConfig()) -> PluginPackage[LoggerConfig]:
35+
"""Returns the Logger plugin package."""
36+
return PluginPackage(module=LoggerModule(config), manifest=manifest)

packages/plugins/polywrap-logger-plugin/polywrap_logger_plugin/py.typed

Whitespace-only changes.
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
[build-system]
2+
requires = ["poetry-core"]
3+
build-backend = "poetry.core.masonry.api"
4+
5+
[tool.poetry]
6+
name = "polywrap-logger-plugin"
7+
version = "0.1.2"
8+
description = ""
9+
authors = ["Niraj <[email protected]>"]
10+
readme = "README.md"
11+
packages = [{include = "polywrap_logger_plugin"}]
12+
13+
[tool.poetry.dependencies]
14+
python = "^3.10"
15+
polywrap-plugin = "0.1.2"
16+
polywrap-core = "^0.1.2"
17+
polywrap-msgpack = "^0.1.2"
18+
polywrap-manifest = "^0.1.2"
19+
20+
[tool.poetry.group.dev.dependencies]
21+
polywrap-client = {path = "../../polywrap-client", develop = true}
22+
polywrap-uri-resolvers = {path = "../../polywrap-uri-resolvers", develop = true}
23+
polywrap-client-config-builder = {path = "../../polywrap-client-config-builder", develop = true}
24+
black = "^23.1.0"
25+
pytest = "^7.2.1"
26+
pytest-asyncio = "^0.20.3"
27+
isort = "^5.12.0"
28+
bandit = "^1.7.4"
29+
pyright = "^1.1.296"
30+
pylint = "^2.16.3"
31+
tox = "^3.26.0"
32+
tox-poetry = "^0.4.1"
33+
pytest-mock = "^3.10.0"
34+
pydocstyle = "^6.3.0"
35+
36+
[tool.bandit]
37+
exclude_dirs = ["tests"]
38+
39+
[tool.black]
40+
target-version = ["py310"]
41+
exclude = "polywrap_logger_plugin/wrap/*"
42+
43+
[tool.pyright]
44+
typeCheckingMode = "strict"
45+
reportShadowedImports = false
46+
exclude = [
47+
"**/wrap/"
48+
]
49+
50+
[tool.pytest.ini_options]
51+
asyncio_mode = "auto"
52+
testpaths = [
53+
"tests",
54+
]
55+
56+
[tool.pylint]
57+
disable = [
58+
"too-many-return-statements",
59+
"unused-argument",
60+
"invalid-name",
61+
"too-few-public-methods",
62+
]
63+
ignore-paths = [
64+
"polywrap_logger_plugin/wrap"
65+
]
66+
67+
[tool.isort]
68+
profile = "black"
69+
multi_line_output = 3
70+
skip = ["polywrap_logger_plugin/wrap"]
71+
72+
[tool.pydocstyle]
73+
# default
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
#import * from "ens/wraps.eth:[email protected]"

packages/plugins/polywrap-logger-plugin/tests/__init__.py

Whitespace-only changes.
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import logging
2+
import pytest
3+
from polywrap_logger_plugin import LoggerConfig, LogLevel
4+
from typing import Callable
5+
6+
7+
@pytest.fixture
8+
def valid_logger_config() -> Callable[[LogLevel], LoggerConfig]:
9+
def _valid_logger_config(level: LogLevel) -> LoggerConfig:
10+
return LoggerConfig(logger=logging.getLogger("test_logger"), level=level)
11+
return _valid_logger_config
12+

packages/plugins/polywrap-logger-plugin/tests/integration/__init__.py

Whitespace-only changes.
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import pytest
2+
3+
from polywrap_client import PolywrapClient
4+
from polywrap_core import Uri
5+
from polywrap_uri_resolvers import FsUriResolver, SimpleFileReader
6+
from polywrap_client_config_builder import PolywrapClientConfigBuilder
7+
from polywrap_logger_plugin import logger_plugin
8+
9+
@pytest.fixture
10+
def builder():
11+
return PolywrapClientConfigBuilder().add_resolver(
12+
FsUriResolver(file_reader=SimpleFileReader())
13+
).set_package(
14+
Uri.from_str("plugin/logger"), logger_plugin()
15+
)
16+
17+
@pytest.fixture
18+
def client(builder: PolywrapClientConfigBuilder):
19+
config = builder.build()
20+
return PolywrapClient(config)
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import logging
2+
from polywrap_client import PolywrapClient
3+
from polywrap_core import Uri
4+
import pytest
5+
from polywrap_logger_plugin import LogLevel
6+
from _pytest.logging import LogCaptureFixture
7+
8+
9+
@pytest.mark.parametrize("log_level", list(LogLevel))
10+
async def test_log_levels(
11+
client: PolywrapClient,
12+
caplog: LogCaptureFixture,
13+
log_level: LogLevel,
14+
):
15+
caplog.set_level(logging.DEBUG)
16+
args_log = dict(message="Test message", level=log_level)
17+
18+
assert client.invoke(
19+
uri=Uri.from_str("plugin/logger"),
20+
method="log",
21+
args=args_log,
22+
)
23+
last_record = caplog.records[-1] if caplog.records else None
24+
25+
if log_level >= LogLevel.INFO:
26+
assert last_record
27+
assert last_record.levelno == (log_level.value + 1) * 10
28+
assert last_record.message == "Test message"
29+
else:
30+
assert last_record is None

packages/plugins/polywrap-logger-plugin/tests/unit/__init__.py

Whitespace-only changes.
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import logging
2+
from polywrap_core import InvokerClient
3+
import pytest
4+
from polywrap_logger_plugin import LoggerConfig, LoggerModule, ArgsLog, LogLevel
5+
from _pytest.logging import LogCaptureFixture
6+
from typing import Callable, cast
7+
8+
9+
@pytest.mark.parametrize("config_level", list(LogLevel))
10+
@pytest.mark.parametrize("log_level", list(LogLevel))
11+
async def test_log_levels(
12+
caplog: LogCaptureFixture,
13+
valid_logger_config: Callable[[LogLevel], LoggerConfig],
14+
config_level: LogLevel,
15+
log_level: LogLevel,
16+
):
17+
caplog.set_level(logging.DEBUG)
18+
logger_module = LoggerModule(valid_logger_config(config_level))
19+
args_log = ArgsLog(message="Test message", level=log_level)
20+
21+
assert logger_module.log(args_log, cast(InvokerClient, None), None)
22+
last_record = caplog.records[-1] if caplog.records else None
23+
24+
if log_level.value >= config_level.value:
25+
assert last_record
26+
assert last_record.levelno == (log_level.value + 1) * 10
27+
assert last_record.message == "Test message"
28+
else:
29+
assert last_record is None
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
[tox]
2+
isolated_build = True
3+
envlist = py310
4+
5+
[testenv]
6+
commands =
7+
pytest tests/
8+
9+
[testenv:lint]
10+
commands =
11+
isort --check-only polywrap_logger_plugin
12+
black --check polywrap_logger_plugin
13+
pylint polywrap_logger_plugin
14+
15+
[testenv:typecheck]
16+
commands =
17+
pyright polywrap_logger_plugin
18+
19+
[testenv:secure]
20+
commands =
21+
bandit -r polywrap_logger_plugin -c pyproject.toml
22+
23+
[testenv:dev]
24+
basepython = python3.10
25+
usedevelop = True
26+
commands =
27+
isort polywrap_logger_plugin
28+
black polywrap_logger_plugin
29+

0 commit comments

Comments
 (0)