Skip to content

Commit

Permalink
0.1.3 release
Browse files Browse the repository at this point in the history
  • Loading branch information
buildMapi committed Feb 5, 2024
1 parent 4e79e0b commit 6893aa7
Show file tree
Hide file tree
Showing 8 changed files with 187 additions and 13 deletions.
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "typed_graph"
version = "0.1.0"
version = "0.1.3"
edition = "2021"
authors = ["lcabyg"]
description = "Staticly typed graph library"
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,5 +17,5 @@ The main guarantees typed_graph provides:
For a comprehensive overview on how to setup and use typed_graph see the json_graph example in the example folder

## Other crates
- migration_handler: Provides a language for auto generating staticly typed schemas
- typed_graph_cli: Provides a language for auto generating staticly typed schemas
- typed_graph_py: Port of typed_graph to python
33 changes: 30 additions & 3 deletions typed_graph_py/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ This is a cross compatible port of [TypedGraph][typed_graph-crates-io].

[![pypi](https://img.shields.io/pypi/v/typed_graph.svg)](https://pypi.org/pypi/typed_graph/)

TypedGraph is build to provide an easy to use interface for manipulating an maintiang graph with a rigid schema.
TypedGraph is build to provide an easy to use interface for manipulating an maintaining graphs with a rigid schema.

## Getting started
install using
Expand All @@ -14,7 +14,7 @@ pip install typed_graph

see the [example folder][examples-git] for information on how to create your first project using TypedGraph.

> Note that making and maintianing large static graphs can be time consuming, so using like [migration_handler][migration_handler-git] will make the library significantly easier to maintian
> Note that making and maintianing large static graphs can be time consuming, so using like [typed_graph_cli][typed_graph_cli-git] will make the library significantly easier to maintian
## Development
To run a local instance of the library:
Expand All @@ -31,4 +31,31 @@ import type_graph

[typed_graph-crates-io]: https://crates.io/crates/typed_graph
[examples-git]: https://github.com/build-aau/typed_graph/tree/master/typed_graph_py/examples "example folder in git"
[migration_handler-git]: https://github.com/build-aau/migration_handler "migration_handler in git"
[typed_graph_cli-git]: https://github.com/build-aau/typed_graph_cli "typed_graph_cli in git"

# Build release
First install twine
```pip install twine```
Then to build the release run:
```python setup.py sdist bdist_wheel```
Then test that the build is valid:
```twine check dist/*```
And finally upload to PyPi with:
```twine upload dist/*```

> To avoid having to retype your initials everytime setup a file in `$HOME/.pypirc`
```
[pypi]
username = <username>
password = <password>
```

> Note: your password will be stored in plain text!
> This is also compatible with PyPi access tokens
```
[pypi]
username = __token__
password = pypi-...
```

3 changes: 2 additions & 1 deletion typed_graph_py/build/lib/typed_graph/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from typed_graph.dependency_traits import StrEnum, IntEnum, RustModel, RustRootModel
from typed_graph.dependency_traits import StrEnum, IntEnum, NestedEnum, RustModel, RustRootModel
from typed_graph.typed_traits import SchemaExt, NodeExt, EdgeExt, TypeStatus, GraphData
from typed_graph.typed_graph import TypedGraph
from typed_graph.generic_graph import GenericGraph, GenericSchema, GenericWeight
Expand All @@ -9,6 +9,7 @@
'EdgeExt',
'StrEnum',
'IntEnum',
'NestedEnum',
'TypeStatus',
'RustModel',
'RustRootModel',
Expand Down
150 changes: 148 additions & 2 deletions typed_graph_py/build/lib/typed_graph/dependency_traits.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
from enum import Enum, EnumMeta
from pydantic import BaseModel, RootModel, model_serializer, model_validator
from typing import Any, Callable
from pydantic._internal._model_construction import ModelMetaclass
import inspect

class Enum_M(EnumMeta):
def __new__(metacls, name: str, bases, classdict, **kwds):
Expand Down Expand Up @@ -49,7 +51,7 @@ class vars:
@model_serializer(mode = 'wrap')
def _serialize(
self,
default: Callable[['RustModel'], dict[str, Any]]
default: Callable [['RustModel'], dict[str, Any]]
) -> dict[str, Any] | Any:
"""
Serialize the model to a dict.
Expand Down Expand Up @@ -107,4 +109,148 @@ def _deserialize(
return ModelInstance

RustModel = make_model(BaseModel)
RustRootModel = make_model(RootModel)
RustRootModel = make_model(RootModel)

class NestedEnumMeta(ModelMetaclass):
def __new__(metacls, name, bases, class_dct, *args, **kwargs):
"""
Create a new enum class with a number og varients as attributes
Each varient has their own class
that inherits all the base classes of the enum except for its pydantic model
"""

# Retrieve varient annotations
annotations = None
for k, v in class_dct.items():
if k == '__annotations__':
annotations = v
# Stop the varients from being made as fields in the enum base model
if '__annotations__' in class_dct:
del class_dct['__annotations__']

enum_class = super().__new__(metacls, name, bases, class_dct, *args, **kwargs)

# Create a constructor on the enum that prevents it from being initialized
def __new__(self, *args, **kwarg):
raise Exception(f'Can\'t initialize enum type {name}')
setattr(enum_class, '__new__', __new__)

# Find all bases clases that the varients should also inherit
varient_bases = []
for enum_base in bases:
if enum_base.__name__ != 'NestedEnum' and not issubclass(enum_base, BaseModel):
varient_bases.append(enum_base)

enum_varients = {}

# Create varients if any are provided
if annotations:
for varient_name, varient_type in annotations.items():
varient_class = NestedEnumMeta.create_varient(varient_name, varient_type, varient_bases, class_dct)

setattr(enum_class, varient_name, varient_class)
enum_varients[varient_name] = varient_class

setattr(enum_class, '_members', enum_varients)
return enum_class

@staticmethod
def create_varient(varient_name, varient_type, varient_bases, class_dct, ):
varient_type_name = f"{class_dct['__qualname__']}.{varient_name}"

if varient_type == str:
# Handle unit varients
class_bases = [RootModel, *varient_bases]
variation_class = ModelMetaclass.__new__(
ModelMetaclass,
varient_type_name,
(RootModel, ),
{
'__module__': class_dct['__module__'],
'__qualname__': varient_type_name,
'__annotations__': {
'root': str,
},
}
)

return variation_class(varient_name)

elif isinstance(varient_type, dict):
# Handle struct varients
class_bases = [RustModel, *varient_bases]

varient_dict = {
'__module__': class_dct['__module__'],
'__qualname__': varient_type_name,
'__annotations__': varient_type
}

# pass information about generic along
if '__orig_bases__' in class_dct:
varient_dict['__orig_bases__'] = class_dct['__orig_bases__']

variation_class = ModelMetaclass.__new__(
ModelMetaclass,
varient_type_name,
tuple(class_bases),
varient_dict
)

return variation_class
else:
raise Exception(f"Unsupported varient type {varient_type} expected {str(str)} or {str(dict)}")

class NestedEnum(BaseModel, metaclass=NestedEnumMeta):

@model_validator(mode = 'wrap')
def _deserialize(
cls,
d: dict[str, Any] | Any,
default: Callable[[dict[str, Any]], 'RustModel']
) -> 'RustModel':
# Handle unit varients
if isinstance(d, str) and d in cls._members:
varient = cls._members[d]
if not inspect.isclass(varient):
return varient.model_validate(d)

# If it is neither, then it must just be the enum
if not isinstance(d, dict):
return default(d)

if len(d) != 1:
return default(d)

# Handle dict varient
varient_name = next(iter(d.keys()))
if varient_name in cls._members:
varient = cls._members[varient_name]
if inspect.isclass(varient):
return varient.model_validate(d[varient_name])

return default(d)

@classmethod
def __class_getitem__(cls, ty):
instance = super().__class_getitem__(ty)

# We only populate _members if it is empty
# This is because Generic reuses the same class
if not hasattr(instance, '_members') or not instance._members :
instance._members = {}

# We now need to propergate the generics from the enum to its varients
for name, _ in cls._members.items():

varient_instance = getattr(instance, name)

# Do not propergate generics to unit enums
if inspect.isclass(varient_instance):
varient_instance = varient_instance[ty]

# Update varients on instance class
setattr(instance, name, varient_instance)
instance._members[name] = varient_instance

return instance
6 changes: 3 additions & 3 deletions typed_graph_py/build/lib/typed_graph/typed_graph.py
Original file line number Diff line number Diff line change
Expand Up @@ -438,19 +438,19 @@ def get_incoming_and_outgoing(self, node_id: NK) -> Iterator[EdgeRef[E, NK]]:
"""
return chain(self.get_outgoing(node_id), self.get_incoming(node_id))

def get_incoming_filter(self, node_id: NK, f: Callable[[ET], bool]) -> Iterator[EdgeRef[E, NK]]:
def get_incoming_filter(self, node_id: NK, f: Callable[[E], bool]) -> Iterator[EdgeRef[E, NK]]:
"""
Filter the incoming edges based on edge type
"""
return filter(lambda e: f(e.weight), self.get_incoming(node_id))

def get_outgoing_filter(self, node_id: NK, f: Callable[[ET], bool]) -> Iterator[EdgeRef[E, NK]]:
def get_outgoing_filter(self, node_id: NK, f: Callable[[E], bool]) -> Iterator[EdgeRef[E, NK]]:
"""
Filter the outgoing edges based on edge type
"""
return filter(lambda e: f(e.weight), self.get_outgoing(node_id))

def get_incoming_and_outgoing_filter(self, node_id: NK, f: Callable[[ET], bool]) -> Iterator[EdgeRef[E, NK]]:
def get_incoming_and_outgoing_filter(self, node_id: NK, f: Callable[[E], bool]) -> Iterator[EdgeRef[E, NK]]:
"""
Filter incoming and outgoing edges based on edge type
"""
Expand Down
2 changes: 1 addition & 1 deletion typed_graph_py/setup.cfg
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[metadata]
name = typed_graph
version = 0.1.2
version = 0.1.3
author = lcabyg
author_email = [email protected]
url = https://github.com/build-aau/typed_graph
Expand Down

0 comments on commit 6893aa7

Please sign in to comment.