Skip to content
This repository has been archived by the owner on Jul 13, 2023. It is now read-only.

Commit

Permalink
Use @DataClass from attr package
Browse files Browse the repository at this point in the history
This makes it a lot easier to define simple classes. Plus, something like it is
going to be standardized soon: https://www.python.org/dev/peps/pep-0557/

Note that an incompatibility between attr and mypy leads to typechecking errors
when using @DataClass constructors because the __init__ function is generated.
A plugin should be on the way: python/mypy#2088
  • Loading branch information
mwillsey committed Dec 1, 2017
1 parent 73b4b3a commit da89660
Show file tree
Hide file tree
Showing 6 changed files with 46 additions and 53 deletions.
1 change: 1 addition & 0 deletions Pipfile
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ networkx = "*"
typing = "*"
PyYAML = "*"
pigpio = "*"
attrs = "*"

[dev-packages]
mypy = "*"
Expand Down
9 changes: 8 additions & 1 deletion Pipfile.lock

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

42 changes: 16 additions & 26 deletions puddle/arch.py
Original file line number Diff line number Diff line change
@@ -1,30 +1,25 @@
import networkx as nx
import yaml

from typing import Tuple, Any, ClassVar, List, Dict
from attr import dataclass
from typing import Tuple, Any, ClassVar, List, Dict, Set

from puddle.util import pairs

import logging
log = logging.getLogger(__name__)


Node = Any
Location = Tuple[int, int]


# disable generation of cmp so it uses id-based hashing
@dataclass(cmp=False)
class Droplet:

def __init__(self, info='a', cells=None):
self.info = info
self.locations: Set[Tuple] = cells or set()
self.valid = True

def __str__(self):
invalid_str = '' if self.valid else 'INVALID, '
return f'Droplet({invalid_str}{self.info!r})'

def __repr__(self):
return f'{self} at 0x{id(self):x}'
info: Any
locations: Set[Location]
valid: bool = True

def to_dict(self):
""" Used to JSONify this for rendering in the client """
Expand Down Expand Up @@ -63,25 +58,21 @@ def mix(self, other: 'Droplet'):
return Droplet(info, self.locations | other.locations)


@dataclass
class Cell:

def __init__(self, id: int, location: Tuple[Node, Node]) -> None:
self.id = id
self.location = location

def __str__(self):
return f'{self.__class__.__name__}({self.location})'
pin: int
location: Location


class Command:
shape: ClassVar[nx.DiGraph]
input_locations: ClassVar[List[Node]]
input_locations: ClassVar[List[Location]]
input_droplets: List[Droplet]
result: Any

strict: ClassVar[bool] = False

def run(self, mapping: Dict[Node, Node]): ...
def run(self, mapping: Dict[Location, Location]): ...


class Move(Command):
Expand Down Expand Up @@ -168,6 +159,7 @@ def run(self, mapping):
class CollisionError(Exception):
pass


class ArchitectureError(Exception):
pass

Expand Down Expand Up @@ -280,13 +272,11 @@ def from_string(cls, string, **kwargs):
data = yaml.load(string)
board = data['board']


h = len(board)
w = max(len(row) for row in board)

empty_values = ['_', None]


# cells keyed by id
cells = {}

Expand Down Expand Up @@ -339,8 +329,8 @@ def from_file(cls, filename, **kwargs):
arch.source_file = filename
return arch

def spec_string(self, with_droplets=False):
""" Return the specification string of this Architecture. """
def to_yaml_string(self, with_droplets=False):
""" Dump the Architecture to YAML string. """

lines = [ [' '] * self.width for _ in range(self.height) ]

Expand Down
3 changes: 0 additions & 3 deletions puddle/execution.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,6 @@
import logging
log = logging.getLogger(__name__)

# simple type aliases
Node = Any


class ExcecutionFailure(Exception):
pass
Expand Down
40 changes: 19 additions & 21 deletions puddle/routing/astar.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@
import itertools
import heapq

from typing import Dict, Set, Tuple, List, Any, Optional
from attr import dataclass, Factory
from typing import Dict, Tuple, List, Any, Optional

from puddle.arch import Location
from puddle.util import manhattan_distance, neighborhood

import networkx as nx
Expand All @@ -12,36 +14,32 @@
log = logging.getLogger(__name__)


Node = Any
Path = List[Node]
Path = List[Location]


class RouteFailure(Exception):
pass


@dataclass(cmp=False)
class Agent:
""" An agent to be routed.
def __init__(self, item, source, target,
collision_group: Optional[int] = None):
""" An agent to be routed.
collision_group of None can never collide with anything.
"""

collision_group of None can never collide with anything.
"""

self.item = item
self.source = source
self.target = target
if collision_group is None:
# create a guaranteed unique group
self.collision_group = object()
else:
self.collision_group = collision_group
item: Any
source: Location
target: Location
# create a guaranteed unique group
collision_group: Optional[int] = Factory(object)


class Router:

def __init__(self, graph: nx.DiGraph) -> None:
graph = nx.DiGraph

def __init__(self, graph) -> None:
self.graph = graph

def route(
Expand Down Expand Up @@ -80,7 +78,7 @@ def route(
return paths

@staticmethod
def build_path(predecessors: Dict[Node, Node], last) -> Path:
def build_path(predecessors: Dict[Location, Location], last) -> Path:
"""Reconstruct a path from the destination and a predecessor map."""
path = []
node = last
Expand Down Expand Up @@ -113,10 +111,10 @@ def a_star(self, agent) -> Path:

# Maps enqueued nodes to distance of discovered paths and the
# computed heuristics to target. Saves recomputing heuristics.
enqueued: Dict[Node, Tuple[int, int]] = {}
enqueued: Dict[Location, Tuple[int, int]] = {}

# Maps explored nodes to its predecessor on the shortest path.
explored: Dict[Node, Node] = {}
explored: Dict[Location, Location] = {}

while todo:
_, _, current, distance, time, parent = pop(todo)
Expand Down
4 changes: 2 additions & 2 deletions tests/test_execution.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@
# NOTE this does a little bit of badness by creating droplets without
# locations. It works fine for now.
@pytest.mark.parametrize('command_cls, droplets', [
(Mix, [Droplet('a'), Droplet('b')]),
(Split, [Droplet('a')]),
(Mix, [Droplet('a', set()), Droplet('b', set())]),
(Split, [Droplet('a', set())]),
])
def test_place(arch, command_cls, droplets):

Expand Down

0 comments on commit da89660

Please sign in to comment.