pw_log_tokenized: Python class for log metadata

Change-Id: I306762cf8869776ec3db148b0ec6899ba6db7cde
Reviewed-on: https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/37322
Commit-Queue: Wyatt Hepler <hepler@google.com>
Reviewed-by: Keir Mierle <keir@google.com>
diff --git a/pw_env_setup/BUILD.gn b/pw_env_setup/BUILD.gn
index e882151..2a4528f 100644
--- a/pw_env_setup/BUILD.gn
+++ b/pw_env_setup/BUILD.gn
@@ -35,6 +35,7 @@
     "$dir_pw_doctor/py",
     "$dir_pw_env_setup/py",
     "$dir_pw_hdlc/py",
+    "$dir_pw_log_tokenized/py",
     "$dir_pw_module/py",
     "$dir_pw_package/py",
     "$dir_pw_presubmit/py",
diff --git a/pw_log_tokenized/BUILD.gn b/pw_log_tokenized/BUILD.gn
index a3151a5..1feee29 100644
--- a/pw_log_tokenized/BUILD.gn
+++ b/pw_log_tokenized/BUILD.gn
@@ -96,4 +96,5 @@
 
 pw_doc_group("docs") {
   sources = [ "docs.rst" ]
+  other_deps = [ "py" ]
 }
diff --git a/pw_log_tokenized/docs.rst b/pw_log_tokenized/docs.rst
index 1c30ee7..e5242b8 100644
--- a/pw_log_tokenized/docs.rst
+++ b/pw_log_tokenized/docs.rst
@@ -3,9 +3,14 @@
 ----------------
 pw_log_tokenized
 ----------------
-``pw_log_tokenized`` is a ``pw_log`` backend that tokenizes log messages using
-the ``pw_tokenizer`` module. Log messages are tokenized and passed to the
-``pw_tokenizer_HandleEncodedMessageWithPayload`` function. For maximum
+The ``pw_log_tokenized`` module contains utilities for tokenized logging. It
+connects ``pw_log`` to ``pw_tokenizer``.
+
+C++ backend
+===========
+``pw_log_tokenized`` provides a backend for ``pw_log`` that tokenizes log
+messages with the ``pw_tokenizer`` module. Log messages are tokenized and passed
+to the ``pw_tokenizer_HandleEncodedMessageWithPayload`` function. For maximum
 efficiency, the log level, 16-bit tokenized module name, and flags bits are
 passed through the payload argument.
 
@@ -41,5 +46,13 @@
 the ``pw_tokenizer:global_handler_with_payload`` facade, which must be
 implemented by the user of ``pw_log_tokenized``.
 
-.. note::
-  The documentation for this module is currently incomplete.
+Python package
+==============
+``pw_log_tokenized`` includes a Python package for decoding tokenized messages
+logs.
+
+pw_log_tokenized
+----------------
+.. automodule:: pw_log_tokenized
+  :members:
+  :undoc-members:
diff --git a/pw_log_tokenized/py/BUILD.gn b/pw_log_tokenized/py/BUILD.gn
new file mode 100644
index 0000000..f8ebd18
--- /dev/null
+++ b/pw_log_tokenized/py/BUILD.gn
@@ -0,0 +1,23 @@
+# Copyright 2021 The Pigweed Authors
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not
+# use this file except in compliance with the License. You may obtain a copy of
+# the License at
+#
+#     https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations under
+# the License.
+
+import("//build_overrides/pigweed.gni")
+
+import("$dir_pw_build/python.gni")
+
+pw_python_package("py") {
+  setup = [ "setup.py" ]
+  sources = [ "pw_log_tokenized/__init__.py" ]
+  pylintrc = "$dir_pigweed/.pylintrc"
+}
diff --git a/pw_log_tokenized/py/pw_log_tokenized/__init__.py b/pw_log_tokenized/py/pw_log_tokenized/__init__.py
new file mode 100644
index 0000000..5c46b7b
--- /dev/null
+++ b/pw_log_tokenized/py/pw_log_tokenized/__init__.py
@@ -0,0 +1,41 @@
+# Copyright 2021 The Pigweed Authors
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not
+# use this file except in compliance with the License. You may obtain a copy of
+# the License at
+#
+#     https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations under
+# the License.
+"""Tools for working with tokenized logs."""
+
+from dataclasses import dataclass
+
+
+def _mask(value: int, start: int, count: int) -> int:
+    mask = (1 << count) - 1
+    return (value & (mask << start)) >> start
+
+
+@dataclass(frozen=True)
+class Metadata:
+    """Parses the metadata payload sent by pw_log_tokenized."""
+    _value: int
+
+    log_bits: int = 6
+    module_bits: int = 16
+    flag_bits: int = 10
+
+    def log_level(self) -> int:
+        return _mask(self._value, 0, self.log_bits)
+
+    def module_token(self) -> int:
+        return _mask(self._value, self.log_bits, self.module_bits)
+
+    def flags(self) -> int:
+        return _mask(self._value, self.log_bits + self.module_bits,
+                     self.flag_bits)
diff --git a/pw_log_tokenized/py/pw_log_tokenized/py.typed b/pw_log_tokenized/py/pw_log_tokenized/py.typed
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/pw_log_tokenized/py/pw_log_tokenized/py.typed
diff --git a/pw_log_tokenized/py/setup.py b/pw_log_tokenized/py/setup.py
new file mode 100644
index 0000000..f1ca09d
--- /dev/null
+++ b/pw_log_tokenized/py/setup.py
@@ -0,0 +1,27 @@
+# Copyright 2021 The Pigweed Authors
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not
+# use this file except in compliance with the License. You may obtain a copy of
+# the License at
+#
+#     https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations under
+# the License.
+"""The pw_log_tokenized package."""
+
+import setuptools  # type: ignore
+
+setuptools.setup(
+    name='pw_log_tokenized',
+    version='0.0.1',
+    author='Pigweed Authors',
+    author_email='pigweed-developers@googlegroups.com',
+    description='Tools for working with tokenized logs',
+    packages=setuptools.find_packages(),
+    package_data={'pw_log_tokenized': ['py.typed']},
+    zip_safe=False,
+)