Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Dev/type updates #87

Merged
merged 2 commits into from
Aug 28, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/python-package.yml
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ jobs:

- run: |
mk python-release owner=vkottler \
repo=runtimepy version=2.0.0
repo=runtimepy version=2.1.0
if: |
matrix.python-version == '3.11'
&& matrix.system == 'ubuntu-latest'
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@
=====================================
generator=datazen
version=3.1.2
hash=31bf81f7cf74ce38be9e121deda89fbe
hash=81f6301e885d2982710c34e69c537077
=====================================
-->

# runtimepy ([2.0.0](https://pypi.org/project/runtimepy/))
# runtimepy ([2.1.0](https://pypi.org/project/runtimepy/))

[![python](https://img.shields.io/pypi/pyversions/runtimepy.svg)](https://pypi.org/project/runtimepy/)
![Build Status](https://github.com/vkottler/runtimepy/workflows/Python%20Package/badge.svg)
Expand Down
2 changes: 1 addition & 1 deletion local/configs/package.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ description: A framework for implementing Python services.
entry: {{entry}}

requirements:
- vcorelib>=2.0.2
- vcorelib>=2.5.4
- websockets
- "windows-curses; sys_platform == 'win32'"

Expand Down
2 changes: 1 addition & 1 deletion local/variables/package.yaml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
major: 2
minor: 0
minor: 1
patch: 0
entry: runtimepy
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta:__legacy__"

[project]
name = "runtimepy"
version = "2.0.0"
version = "2.1.0"
description = "A framework for implementing Python services."
readme = "README.md"
requires-python = ">=3.8"
Expand Down
4 changes: 2 additions & 2 deletions runtimepy/__init__.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# =====================================
# generator=datazen
# version=3.1.2
# hash=25c862b538abb92065ece95c39caae77
# hash=e2256d2409dc864fe3a4f05643274ad3
# =====================================

"""
Expand All @@ -10,4 +10,4 @@

DESCRIPTION = "A framework for implementing Python services."
PKG_NAME = "runtimepy"
VERSION = "2.0.0"
VERSION = "2.1.0"
7 changes: 6 additions & 1 deletion runtimepy/codec/protocol/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -182,10 +182,15 @@ def value(self, name: str, resolve_enum: bool = True) -> ProtocolPrimitive:

return self._fields.get(name, resolve_enum=resolve_enum)

@property
def size(self) -> int:
"""Get this protocol's size in bytes."""
return self.array.length()

def __str__(self) -> str:
"""Get this instance as a string."""

return f"({self.array.size})\t" + "\t".join(
return f"({self.size})\t" + "\t".join(
f"{name}={self[name]}" for name in self._names.registered_order
)

Expand Down
134 changes: 105 additions & 29 deletions runtimepy/codec/system/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,19 @@
"""

# built-in
from typing import Dict
from typing import Dict, Optional, Type

# third-party
from vcorelib.namespace import CPP_DELIM, Namespace

# internal
from runtimepy import PKG_NAME
from runtimepy.codec.protocol import Protocol
from runtimepy.enum import RuntimeEnum
from runtimepy.enum.registry import EnumRegistry
from runtimepy.enum.registry import (
DEFAULT_ENUM_PRIMITIVE,
EnumRegistry,
RuntimeIntEnum,
)
from runtimepy.primitives.byte_order import ByteOrder
from runtimepy.primitives.type import AnyPrimitiveType, PrimitiveTypes

Expand All @@ -24,56 +28,128 @@ def __init__(self, *namespace: str) -> None:

self.primitives: Dict[str, AnyPrimitiveType] = {}
self.custom: Dict[str, Protocol] = {}
self._enums = EnumRegistry()

global_namespace = Namespace(delim=CPP_DELIM)

# Register global names.
for name, kind in PrimitiveTypes.items():
self.primitives[global_namespace.namespace(name)] = kind

self.root_namespace = global_namespace.child(*namespace)
self.root_namespace = global_namespace

# Register enums.
self._enums = EnumRegistry()
self.runtime_enum(
"ByteOrder", ByteOrder.register_enum(self._enums, name="ByteOrder")
)
with self.root_namespace.pushed(PKG_NAME):
for enum in [ByteOrder]:
self.runtime_int_enum(enum)

def register(self, name: str) -> Protocol:
self.root_namespace = global_namespace.child(*namespace)

def runtime_int_enum(self, enum: Type[RuntimeIntEnum]) -> None:
"""Register an enumeration class."""

name = self._name(enum.enum_name(), check_available=True)
runtime = enum.register_enum(self._enums, name=name)
self._register_primitive(name, runtime.primitive)

def enum(
self,
name: str,
items: Dict[str, int],
*namespace: str,
primitive: str = DEFAULT_ENUM_PRIMITIVE,
) -> None:
"""Register an enumeration."""

name = self._name(name, *namespace, check_available=True)

enum = self._enums.enum(name, "int", items=items, primitive=primitive)
assert enum is not None
self._register_primitive(name, enum.primitive)

def register(self, name: str, *namespace: str) -> Protocol:
"""Register a custom type."""

new_type = Protocol(self._enums)
name = self.root_namespace.namespace(name)
self.custom[name] = new_type
self.custom[
self._name(name, *namespace, check_available=True)
] = new_type
return new_type

def runtime_enum(self, name: str, enum: RuntimeEnum) -> bool:
"""Register an enumeration."""
def add(self, custom_type: str, field_name: str, field_type: str) -> None:
"""Add a field to a custom type."""

name = self.root_namespace.namespace(name)
type_name = self._find_name(custom_type, strict=True)
assert type_name is not None
field_type_name = self._find_name(field_type, strict=True)
assert field_type_name is not None

result = self._enums.register(name, enum)
assert type_name in self.custom, type_name
custom = self.custom[type_name]

assert name not in self.primitives, name
self.primitives[name] = PrimitiveTypes[enum.primitive]
# Handle enumerations.
enum = self._enums.get(field_type_name)
if enum is not None:
custom.add_field(field_name, enum=field_type_name)
return

# Lookup field type.
if field_type_name in self.custom:
custom.array.add_to_end(self.custom[field_type_name].array)
return

custom.add_field(
field_name, kind=self.primitives[field_type_name].name
)

def _find_name(
self, name: str, *namespace: str, strict: bool = False
) -> Optional[str]:
"""Attempt to find a registered name."""

if name in self.primitives:
return name

with self.root_namespace.pushed(*namespace):
matches = list(self.root_namespace.search(pattern=name))

assert (
0 <= len(matches) <= 1
), f"Duplicate type names! {name}: {matches}"

assert not strict or matches, f"Name '{name}' not found."

return matches[0] if matches else None

def _name(
self, name: str, *namespace: str, check_available: bool = False
) -> str:
"""Resolve a given name against the current namespace."""

with self.root_namespace.pushed(*namespace):
if check_available:
resolved = self._find_name(name)
assert (
resolved is None
), f"Name '{name}' not available! found '{resolved}'"

result = self.root_namespace.namespace(name)

return result

def enum(
self, name: str, items: Dict[str, int], primitive: str = "uint8"
) -> None:
"""Register an enumeration."""
def _register_primitive(self, name: str, kind: str) -> None:
"""Register a type alias for a primitive value."""

self._enums.enum(name, "int", items=items, primitive=primitive)
assert name not in self.primitives, name
self.primitives[name] = PrimitiveTypes[kind]

def size(self, name: str) -> int:
def size(self, name: str, *namespace: str) -> int:
"""Get the size of a named type."""

matches = list(self.root_namespace.search(pattern=name))
assert len(matches) == 1, f"Duplicate type names {name}: {matches}"
name = matches[0]
found = self._find_name(name, *namespace, strict=True)
assert found is not None

if name in self.primitives:
return self.primitives[name].size
if found in self.primitives:
return self.primitives[found].size

return self.custom[name].array.size
return self.custom[found].size
2 changes: 1 addition & 1 deletion runtimepy/data/schemas/EnumRegistry.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,5 @@ type: object
additionalProperties: false

patternProperties:
"^\\w+$":
"^[\\w\\:]+$":
$ref: package://runtimepy/schemas/RuntimeEnum.yaml
20 changes: 17 additions & 3 deletions runtimepy/enum/registry.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
from runtimepy.mapping import EnumMappingData as _EnumMappingData
from runtimepy.registry import Registry as _Registry

DEFAULT_ENUM_PRIMITIVE = "uint8"


class EnumRegistry(_Registry[_RuntimeEnum]):
"""A runtime enumeration registry."""
Expand All @@ -31,7 +33,7 @@ def enum(
name: str,
kind: _EnumTypelike,
items: _EnumMappingData = None,
primitive: str = "uint8",
primitive: str = DEFAULT_ENUM_PRIMITIVE,
) -> _Optional[_RuntimeEnum]:
"""Create a new runtime enumeration."""

Expand All @@ -44,6 +46,16 @@ def enum(
class RuntimeIntEnum(_IntEnum):
"""An integer enumeration extension."""

@classmethod
def primitive(cls) -> str:
"""The underlying primitive type for this runtime enumeration."""
return DEFAULT_ENUM_PRIMITIVE

@classmethod
def enum_name(cls) -> str:
"""Get a name for this enumeration."""
return cls.__name__

@classmethod
def runtime_enum(cls, identifier: int) -> _RuntimeEnum:
"""Obtain a runtime enumeration from this class."""
Expand All @@ -56,8 +68,10 @@ def register_enum(
"""Register an enumeration to a registry."""

if name is None:
name = cls.__name__
name = cls.enum_name()

result = registry.register_dict(name, _RuntimeEnum.data_from_enum(cls))
data = _RuntimeEnum.data_from_enum(cls)
data["primitive"] = cls.primitive()
result = registry.register_dict(name, data)
assert result is not None
return result
2 changes: 1 addition & 1 deletion runtimepy/mixins/regex.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
from re import Pattern as _Pattern
from re import compile as _compile

DEFAULT_PATTERN = _compile("^\\w+$")
DEFAULT_PATTERN = _compile("^[\\w\\:]+$")
CHANNEL_PATTERN = _compile("^[a-z0-9-_.]+$")


Expand Down
2 changes: 1 addition & 1 deletion runtimepy/requirements.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
vcorelib>=2.0.2
vcorelib>=2.5.4
websockets
windows-curses; sys_platform == 'win32'
Loading