From d2a52facbd9bb913e54d8cd6ba119451df4592d0 Mon Sep 17 00:00:00 2001 From: Jay Date: Thu, 30 Oct 2025 21:56:42 +0000 Subject: [PATCH 1/2] Enhance functionality and documentation: - Add `wire_partial` function for pre-applying arguments in wire functions. - Introduce `StepperException` for error handling in stepper execution. - Update `write_graphviz` to support view and format options. - Implement tests for new features and edge cases. - Document kwargs bubbling for cleaner function signatures in graph pipelines. --- .vscode/settings.json | 7 + docs/kwargs-bubbling.md | 121 +++++++++++ src/hyperway/__init__.py | 3 +- src/hyperway/edges.py | 41 +++- src/hyperway/graph/base.py | 4 +- src/hyperway/packer.py | 2 +- src/hyperway/reader.py | 2 +- src/hyperway/stepper.py | 17 +- src/hyperway/writer.py | 8 +- tests/test_edges.py | 342 +++++++++++++++++++++++++++++++- tests/test_stepper_advanced.py | 5 +- tests/test_writer.py | 132 ++++++++++++ workspace/order-of-operation.py | 3 +- workspace/run_2.gv | 38 ++-- workspace/run_2.gv.png | Bin 27225 -> 27309 bytes 15 files changed, 690 insertions(+), 35 deletions(-) create mode 100644 .vscode/settings.json create mode 100644 docs/kwargs-bubbling.md diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..9b38853 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,7 @@ +{ + "python.testing.pytestArgs": [ + "tests" + ], + "python.testing.unittestEnabled": false, + "python.testing.pytestEnabled": true +} \ No newline at end of file diff --git a/docs/kwargs-bubbling.md b/docs/kwargs-bubbling.md new file mode 100644 index 0000000..7891c9d --- /dev/null +++ b/docs/kwargs-bubbling.md @@ -0,0 +1,121 @@ +# Kwargs Bubbling + +Wire functions can pass metadata through the graph without polluting function signatures. This is _kwargs bubbling_ - setup kwargs flow through the pipeline in the `ArgsPack`, independent of runtime function calls. + +## The Problem + +Consider passing metadata through a graph pipeline: + +```py +def add(x, y, **metadata): + # Function forced to accept **metadata + # even though it doesn't use it + return x + y + +def multiply(value, **metadata): + # Every function needs **metadata + return value * 2 +``` + +This is noisy. Functions have to accept kwargs they don't use. + +## Kwargs Bubbling + +Wire functions separate runtime kwargs from pipeline metadata: + +```py +from hyperway.edges import wire + +def add(x, y): + # Clean signature + return x + y + +def multiply(value): + # No **kwargs noise + return value * 2 + +# Setup wire with pipeline metadata +wire_add = wire(add, stage='sum', pipeline='data-transform') +wire_mul = wire(multiply, stage='amplify') +``` + +When called: + +```py +result = wire_add(10, 20) +# Function receives: add(10, 20) +# ArgsPack contains: (30, stage='sum', pipeline='data-transform') +``` + +The metadata _bubbles_ through the `ArgsPack`, never touching the function signature. + +## Call-time vs Setup-time + +Two distinct flows: + +**Call-time kwargs** → Go to the function +```py +def add_with_multiplier(x, y, multiplier=1): + return (x + y) * multiplier + +wire_func = wire(add_with_multiplier, pipeline='test') +result = wire_func(10, 20, multiplier=5) +# Function receives: add_with_multiplier(10, 20, multiplier=5) +# ArgsPack contains: (150, pipeline='test') +``` + +**Setup-time kwargs** → Bubble through `ArgsPack` +```py +wire_func = wire(add, stage='sum', meta='important') +result = wire_func(10, 20) +# Function receives: add(10, 20) +# ArgsPack contains: (30, stage='sum', meta='important') +``` + +## Usage in Graphs + +This keeps graph pipelines clean while preserving metadata for debugging, logging, or conditional execution: + +```py +from hyperway.edges import make_edge + +g = Graph() + +# Each wire carries stage metadata +edge1 = g.add(node_a, node_b, through=wire(transform, stage='normalize')) +edge2 = g.add(node_b, node_c, through=wire(validate, stage='check')) + +# Metadata flows through without touching function signatures +g.stepper_prepare(node_a, initial_data) +stepper = g.stepper() + +for rows in stepper: + for caller, argpack in rows: + # Access bubbled metadata + print(f"Stage: {argpack.kwargs.get('stage')}") +``` + +## wire vs wire_partial + +Both support kwargs bubbling: + +**`wire`** - No pre-applied args, just metadata: +```py +wire_func = wire(my_function, pipeline='prod', version='2.0') +result = wire_func(x, y) # Clean call +``` + +**`wire_partial`** - Pre-applied args AND result goes to function: +```py +wire_func = wire_partial(my_function, preset_arg, foo=2) +result = wire_func(runtime_arg, foo=5) # foo=5 overrides foo=2 +# Function receives all merged kwargs +``` + +## Key Insight + +Kwargs bubbling separates concerns: +- **Function signature** = What the function needs to compute +- **Pipeline metadata** = What the graph needs to track + +Functions stay clean. Metadata flows through. The graph stays debuggable. diff --git a/src/hyperway/__init__.py b/src/hyperway/__init__.py index 96e6674..7aa4113 100644 --- a/src/hyperway/__init__.py +++ b/src/hyperway/__init__.py @@ -5,4 +5,5 @@ from .nodes import as_unit, as_units, Unit from .edges import make_edge, Connection from .graph import Graph -from .packer import argspack, argpack, ArgsPack \ No newline at end of file +from .packer import argspack, argpack, ArgsPack +from .stepper import StepperException \ No newline at end of file diff --git a/src/hyperway/edges.py b/src/hyperway/edges.py index 5cecae8..04ca630 100755 --- a/src/hyperway/edges.py +++ b/src/hyperway/edges.py @@ -15,7 +15,36 @@ def is_edge(unit): return isinstance(unit, Connection) -def wire(func, *a, **kw): +def wire_partial(func, *wire_a, **wire_kw): + """Wire function that accepts a standard function, to + later execute as a wire function. + + This _partial_ variant acts like a funtools.partial, allowing + pre-setting of arguments to the wire function. + + def my_wire_func(*a, **kw): + print('WIRING:', a, kw) + return argspack(*a, **kw) + + wire_partial(my_wire_func, 10, 20, foo=2, egg=True) + my_wire_func(30, 40, foo=5) + WIRING: (10, 20, 30, 40) {'foo': 5, 'egg': True} + + Allowing the developer to apply generic functions as a wire + method without handling the argpack. + + c = make_edge(f.add_1, f.add_2, + through=wire(f.mul_5)) + """ + def wrapper(*a, **kw): + """Wrapper wire function.""" + wire_kw.update(kw) + result = func(*wire_a, *a, **wire_kw) + return argspack(result) + return wrapper + + +def wire(func, **wkw): """Wire function that accepts a standard function, to later execute as a wire function @@ -24,11 +53,15 @@ def wire(func, *a, **kw): c = make_edge(f.add_1, f.add_2, through=wire(f.mul_5)) + + The wrapped function is called with all runtime args/kwargs, allowing + it to work with its normal signature. Wire-setup kwargs (wkw) flow + through the ArgsPack for pipeline metadata. """ def wrapper(*a, **kw): """Wrapper wire function.""" result = func(*a, **kw) - return argspack(result, **kw) + return argspack(result, **wkw) return wrapper @@ -105,8 +138,8 @@ def __repr__(self): def __call__(self, *a, _graph=None, **kw): """Call this connection, returning the result of B. This will call A, then the through function (if any), then B.""" - g = _graph or self.on - return self.get_a().process(*a, **kw) + g = self.on if _graph is None else _graph + return self.get_a(g).process(*a, **kw) @property def merge_node(self): diff --git a/src/hyperway/graph/base.py b/src/hyperway/graph/base.py index 5133835..bf8a10d 100644 --- a/src/hyperway/graph/base.py +++ b/src/hyperway/graph/base.py @@ -18,7 +18,9 @@ def resolve_node_connections(self, other): return self.resolve_node_to_nowhere(other) return res - def resolve_node_to_nowhere(self, other): + def resolve_node_to_nowhere(self, other): # py-lint: disable=unused-argument + """Given a node with no connections, return a connection to nowhere. + Override in subclasses to provide custom behaviour.""" return () diff --git a/src/hyperway/packer.py b/src/hyperway/packer.py index 6abbe21..8e5975b 100644 --- a/src/hyperway/packer.py +++ b/src/hyperway/packer.py @@ -67,7 +67,7 @@ def argpack(result=UNDEFINED, *more, **extra): def test_argpack(): a = (1,3,4,5) - d = dict(foo=3, bar=4) + d = {'foo': 3, 'bar': 4} v = argpack(argpack(a, **d)) assert a == v.args[0] diff --git a/src/hyperway/reader.py b/src/hyperway/reader.py index f98233f..66197ab 100644 --- a/src/hyperway/reader.py +++ b/src/hyperway/reader.py @@ -11,7 +11,7 @@ def thin_graph(graph): def flat_graph(graph): - res = tuple() + res = () thin = thin_graph(graph) for node_a, b_nodes in thin.items(): for node_b in b_nodes: diff --git a/src/hyperway/stepper.py b/src/hyperway/stepper.py index df500a0..1996ca0 100644 --- a/src/hyperway/stepper.py +++ b/src/hyperway/stepper.py @@ -7,6 +7,12 @@ is_edge, PartialConnection, get_connections) from .graph.base import is_graph + +class StepperException(Exception): + """Exception raised when stepper encounters an error during execution.""" + pass + + def run_stepper(g, unit_a, val): """Here we can _run_ the graph, executing the chain of connections from A to Z. The _result_ is the end attribute. This may be a graph of @@ -93,6 +99,7 @@ def set_global_expand(expand_func): global expand expand = expand_func + def stepper_c(graph, start_node, argspack): stepper = StepperC(graph) res = stepper.start(start_node, akw=argspack) @@ -191,7 +198,7 @@ def step(self, rows=None, count=1): st_nodes = self.start_nodes if st_nodes is None: # Start node must be something... - raise Exception('start_nodes is None') + raise StepperException('start_nodes is None') self.rows = rows or self.rows or expand(st_nodes, self.start_akw,) while c < count: @@ -364,7 +371,7 @@ def call_one(self, func, akw): return self.call_one_fallthrough(func, akw) - def call_one_fallthrough(self, thing, akw): + def call_one_fallthrough(self, thing, akw): # pylint: disable=unused-argument """ The given function is not A Connection, PartialConnection, Unit, or function (a callable). The last-stage action should occur. @@ -467,6 +474,12 @@ def no_branch(self, func, akw): return self.end_branch(func, akw) def end_branch(self, func, akw): + """The end_branch method is default hanlder when no branches exist on the stepper chain. + This captures the result from the last call, and stores it in the stash for later retrieval. + + return an empty tuple if the stash branch stash ends, else return a tuple of tuples (a row set) + with no destination node. + """ # print(' ... Connections end ...', akw) # A tuple of rows if self.stash_ends: diff --git a/src/hyperway/writer.py b/src/hyperway/writer.py index 9d152ef..d57ea40 100644 --- a/src/hyperway/writer.py +++ b/src/hyperway/writer.py @@ -42,6 +42,8 @@ def write_graphviz(graph, title, **opts): styles = opts.pop('styles', {}) direction = opts.pop('direction', 'TB') + show_view = opts.pop('view', False) + t_format = opts.pop('format', None) print(f'{direction=}') defaults = { # 'format':'svg', @@ -85,8 +87,10 @@ def write_graphviz(graph, title, **opts): t.attr(fontsize=styles.get('fontsize', '12')) t.attr(bgcolor=styles.get('bgcolor', "#00000000")) - # t.view() - # t.format = 'svg' + if show_view: + t.view() + if t_format is not None: + t.format = t_format r_opts = {} directory = opts.get('directory', None) if directory is not None: diff --git a/tests/test_edges.py b/tests/test_edges.py index b30c020..eb22fdb 100644 --- a/tests/test_edges.py +++ b/tests/test_edges.py @@ -9,7 +9,7 @@ import unittest from unittest.mock import patch, MagicMock -from hyperway.edges import make_edge, is_edge, Connection, PartialConnection, as_connections, get_connections +from hyperway.edges import make_edge, is_edge, Connection, PartialConnection, as_connections, get_connections, wire_partial, wire from hyperway.nodes import as_unit from hyperway.packer import argspack, ArgsPack from hyperway.graph import Graph @@ -542,6 +542,92 @@ def func_with_kwargs(a, b=0): # Should execute node A with kwargs (10 + 5 = 15) self.assertEqual(result, 15) + + def test_connection_call_with_explicit_graph(self): + """Connection.__call__ accepts explicit _graph parameter. + + The __call__ method should accept and use an explicit _graph + parameter. This is the primary way to provide graph context for + connection execution, as edge.on is typically None unless explicitly set. + """ + g = Graph() + + # Create connection - note edge.on will be None + edge = g.add(add_ten, add_twenty) + + # Call with explicit graph + with patch('builtins.print'): # Suppress debug output + result = edge(5, _graph=g) + + # Should execute successfully using provided graph + self.assertEqual(result, 15) + + def test_connection_call_without_graph_uses_fallback(self): + """Connection.__call__ works without graph when not needed. + + When _graph is not provided and edge.on is None, the connection + can still execute if the node doesn't require graph resolution. + This tests the `g = _graph or self.on` fallback logic. + """ + g = Graph() + edge = g.add(add_ten, add_twenty) + + # edge.on is None by default + self.assertIsNone(edge.on) + + # Call without _graph should still work (g will be None) + with patch('builtins.print'): # Suppress debug output + result = edge(5) + + # Should execute successfully even without graph reference + self.assertEqual(result, 15) + + def test_connection_call_graph_priority_logic(self): + """Connection.__call__ _graph parameter takes priority over edge.on. + + When both self.on and _graph are provided, the explicit _graph + parameter should take precedence. This verifies the logic: + g = self.on if _graph is None else _graph + + This uses explicit None checking, so even an empty Graph (which is + falsy) will be used when explicitly passed as _graph parameter. + """ + g1 = Graph() # Will have connections + g2 = Graph() # Empty graph + + # Create connection and manually set edge.on + edge = g1.add(add_ten, add_twenty) + edge.on = g1 # Manually set for testing + + # Verify edge.on is set + self.assertIs(edge.on, g1) + + # Test the priority logic: self.on if _graph is None else _graph + # Simulate what happens in Connection.__call__ with different _graph values + + # Scenario 1: _graph is None -> should use edge.on + _graph_param = None + resolved = edge.on if _graph_param is None else _graph_param + self.assertIs(resolved, g1) # _graph is None, so edge.on (g1) is used + + # Scenario 2: _graph is g2 (even though empty) -> should use g2 + _graph_param = g2 + resolved = edge.on if _graph_param is None else _graph_param + self.assertIs(resolved, g2) # _graph is not None, so g2 takes priority + + # Scenario 3: _graph is g1 -> should use g1 + _graph_param = g1 + resolved = edge.on if _graph_param is None else _graph_param + self.assertIs(resolved, g1) # _graph is not None, so g1 takes priority + + # Verify execution works with explicit empty graph + with patch('builtins.print'): # Suppress debug output + result_with_g2 = edge(5, _graph=g2) # Explicit empty graph + result_without_graph = edge(5) # Should use edge.on (g1) + + # Both should execute successfully + self.assertEqual(result_with_g2, 15) + self.assertEqual(result_without_graph, 15) class TestPartialConnectionCall(unittest.TestCase): """Test PartialConnection.__call__ method - direct partial invocation.""" @@ -874,3 +960,257 @@ def test_connection_get_node_key_with_graph(self): node_b_with_graph = edge.get_node_key('b', graph=g) self.assertEqual(node_b_with_graph, edge.b) + +class TestWirePartial(unittest.TestCase): + """Test wire_partial function - partial application for wire functions.""" + + def test_wire_partial_basic_functionality(self): + """wire_partial creates a wrapper that pre-applies arguments. + + wire_partial should act like functools.partial, allowing pre-setting + of arguments to a wire function. The wrapper should return an ArgsPack. + + Example from docstring: + wire_partial(my_wire_func, 10, 20) + # Later called with: (30, 40) + # Results in: my_wire_func(10, 20, 30, 40) + """ + def my_wire_func(*a, **kw): + # Sum all args and kwargs for testing + return sum(a) + sum(kw.values()) + + # Pre-apply arguments 10, 20 + partial_wire = wire_partial(my_wire_func, 10, 20) + + # Call with additional arguments 30, 40 + result = partial_wire(30, 40) + + # Should execute: my_wire_func(10, 20, 30, 40) = 100 + self.assertIsInstance(result, ArgsPack) + self.assertEqual(result.args, (100,)) + + def test_wire_partial_with_kwargs(self): + """wire_partial pre-applies keyword arguments correctly. + + Pre-set keyword arguments should be passed to the underlying function, + with later kwargs merged (later values override pre-set ones). + + Example from docstring: + wire_partial(my_wire_func, 10, 20, foo=2, egg=True) + my_wire_func(30, 40, foo=5) + WIRING: (10, 20, 30, 40) {'foo': 5, 'egg': True} + """ + def my_wire_func(*a, **kw): + return (a, kw) + + # Pre-apply foo=2, egg=True + partial_wire = wire_partial(my_wire_func, 10, 20, foo=2, egg=True) + + # Call with additional kwargs (foo=5 overrides foo=2) + result = partial_wire(30, 40, foo=5) + + # + self.assertIsInstance(result, ArgsPack) + args_result, kwargs_result = result.args, result.kwargs + + self.assertEqual(args_result, (10, 20, 30, 40)) + self.assertEqual(kwargs_result['foo'], 5) # Overridden + self.assertEqual(kwargs_result['egg'], True) # Preserved + + def test_wire_partial_returns_argspack(self): + """wire_partial wrapper always returns an ArgsPack. + + The wrapper must return argspack(result) to maintain compatibility + with the graph execution pipeline. This is the key difference from + raw functools.partial. + """ + def simple_func(x): + return x * 2 + + partial_wire = wire_partial(simple_func, 5) + result = partial_wire() + + # Must be ArgsPack, not raw result + self.assertIsInstance(result, ArgsPack) + self.assertEqual(result.args, (10,)) + + def test_wire_partial_in_connection(self): + """wire_partial can be used as a through function in make_edge. + + This is the primary use case: wrapping a standard function to use + as a wire transform between nodes, with some arguments pre-applied. + """ + def multiplier(pre_mul, value): + return value * pre_mul + + # Create wire that pre-applies multiplier of 5 + wire_mul_5 = wire_partial(multiplier, 5) + + # Create edge with wire function + edge = make_edge(add_one, add_two, through=wire_mul_5) + + # Test with pluck: add_one(10) = 11, mul_5(11) = 55, add_two(55) = 57 + result = edge.pluck(10) + self.assertEqual(result, 57) + + def test_wire_partial_no_args(self): + """wire_partial with no pre-applied args still wraps in ArgsPack. + + Even without pre-applied arguments, wire_partial should create + a wrapper that returns an ArgsPack, making it suitable for use + as a wire function. + """ + def simple_func(x, y): + return x + y + + # No pre-applied args + partial_wire = wire_partial(simple_func) + result = partial_wire(10, 20) + + self.assertIsInstance(result, ArgsPack) + self.assertEqual(result.args, (30,)) + + def test_wire_partial_merges_distinct_kwargs(self): + """wire_partial merges kwargs from pre-set and call-time. + + Pre-set kwargs and call-time kwargs are merged together. + Note: duplicate keys will raise TypeError (Python limitation). + """ + def collect_kwargs(**kw): + return kw + + # Pre-set a, b, c + partial_wire = wire_partial(collect_kwargs, a=1, b=2, c=3) + + # Add distinct kwargs d, e + result = partial_wire(d=4, e=5) + + self.assertIsInstance(result, ArgsPack) + kwargs_result = result.args[0] + + self.assertEqual(kwargs_result['a'], 1) # Pre-set + self.assertEqual(kwargs_result['b'], 2) # Pre-set + self.assertEqual(kwargs_result['c'], 3) # Pre-set + self.assertEqual(kwargs_result['d'], 4) # Call-time + self.assertEqual(kwargs_result['e'], 5) # Call-time + + def test_wire_partial_with_complex_transform(self): + """wire_partial can pre-apply complex transformation parameters. + + Demonstrates using wire_partial to configure a data transformation + function with specific parameters before using it in a graph. + """ + def transform(prefix, suffix, value): + return f"{prefix}_{value}_{suffix}" + + # Create a specific transformer + wire_format = wire_partial(transform, "START", "END") + + edge = make_edge(passthrough, passthrough, through=wire_format) + + # passthrough("hello") = "hello", transform("START", "END", "hello") + result = edge.pluck("hello") + self.assertEqual(result, "START_hello_END") + + +class TestWire(unittest.TestCase): + """Test wire function - simple wrapper for standard functions as wire functions.""" + + def test_wire_basic_functionality(self): + """wire wraps a standard function to return ArgsPack. + + Unlike wire_partial, wire does not pre-apply arguments. It simply + wraps a function to ensure it returns an ArgsPack. + """ + def multiply_by_5(value): + return value * 5 + + wire_func = wire(multiply_by_5) + result = wire_func(10) + + self.assertIsInstance(result, ArgsPack) + self.assertEqual(result.args, (50,)) + + def test_wire_preserves_kwargs(self): + """wire wrapper passes call-time kwargs to func, setup kwargs to ArgsPack. + + The wire wrapper passes runtime args/kwargs to the wrapped function + with its normal signature. Wire-setup kwargs (from wire(func, **wkw)) + flow through the ArgsPack for pipeline metadata. + + This allows clean function calls without needing **kwargs in the signature. + """ + def add_values(x, y): + # Clean function signature - no **kwargs needed! + return x + y + + # Create wire with setup metadata + wire_func = wire(add_values, pipeline_meta='test', stage='transform') + + # Call with normal function args + result = wire_func(10, 20) + + self.assertIsInstance(result, ArgsPack) + self.assertEqual(result.args, (30,)) + # Wire-setup kwargs appear in ArgsPack + self.assertEqual(result.kwargs, {'pipeline_meta': 'test', 'stage': 'transform'}) + + def test_wire_call_time_kwargs_to_function(self): + """wire passes call-time kwargs to the wrapped function. + + Call-time kwargs are passed directly to the function, allowing + normal function signatures with keyword arguments. + """ + def add_with_default(x, y, multiplier=1): + return (x + y) * multiplier + + wire_func = wire(add_with_default, metadata='test') + + # Call with keyword argument + result = wire_func(10, 20, multiplier=2) + + self.assertIsInstance(result, ArgsPack) + self.assertEqual(result.args, (60,)) # (10 + 20) * 2 + # Only wire-setup kwargs in ArgsPack + self.assertEqual(result.kwargs, {'metadata': 'test'}) + + def test_wire_in_connection(self): + """wire can be used as a through function in make_edge. + + Example from docstring: + c = make_edge(f.add_1, f.add_2, through=wire(f.mul_5)) + """ + mul_5 = add_n(0) # Create a simple function + def mul_5_func(v): + return v * 5 + + wire_func = wire(mul_5_func) + edge = make_edge(add_one, add_two, through=wire_func) + + # add_one(10) = 11, mul_5(11) = 55, add_two(55) = 57 + result = edge.pluck(10) + self.assertEqual(result, 57) + + def test_wire_vs_wire_partial(self): + """wire and wire_partial have different use cases. + + wire: wraps a function without pre-applying args + wire_partial: wraps a function WITH pre-applied args + """ + def add_numbers(a, b): + return a + b + + # wire: no pre-applied args + wire_add = wire(add_numbers) + result1 = wire_add(10, 20) + self.assertEqual(result1.args, (30,)) + + # wire_partial: pre-apply first arg + wire_partial_add = wire_partial(add_numbers, 10) + result2 = wire_partial_add(20) + self.assertEqual(result2.args, (30,)) + + # Both return same result but called differently + self.assertEqual(result1.args, result2.args) + + diff --git a/tests/test_stepper_advanced.py b/tests/test_stepper_advanced.py index 0469107..c88747d 100644 --- a/tests/test_stepper_advanced.py +++ b/tests/test_stepper_advanced.py @@ -20,6 +20,7 @@ from hyperway.edges import make_edge, PartialConnection from hyperway.stepper import ( StepperC, + StepperException, StepperIterator, run_stepper, process_forward, @@ -571,12 +572,12 @@ class TestStepperEdgeCases(unittest.TestCase): """Test edge cases and error conditions.""" def test_step_without_start_nodes_raises(self): - """Test that stepping without start_nodes raises an exception.""" + """Test that stepping without start_nodes raises a StepperException.""" g = Graph() stepper = StepperC(g) # Don't set start_nodes - with self.assertRaises(Exception) as context: + with self.assertRaises(StepperException) as context: stepper.step() self.assertIn('start_nodes is None', str(context.exception)) diff --git a/tests/test_writer.py b/tests/test_writer.py index 5efb655..e35fc4c 100644 --- a/tests/test_writer.py +++ b/tests/test_writer.py @@ -251,6 +251,138 @@ def test_write_graphviz_adds_nodes_and_edges(self, mock_digraph_class): # Should add edges self.assertGreater(mock_digraph.edge.call_count, 0) + @patch('hyperway.writer.graphviz.Digraph') + def test_write_graphviz_with_show_view(self, mock_digraph_class): + """Test that view() is called when show_view=True.""" + mock_digraph = MagicMock() + mock_digraph.render.return_value = "test.gv" + mock_digraph_class.return_value = mock_digraph + + write_graphviz(self.graph, "test", view=True) + + # Should call view() when view=True + mock_digraph.view.assert_called_once() + mock_digraph.render.assert_called_once() + + @patch('hyperway.writer.graphviz.Digraph') + def test_write_graphviz_without_show_view(self, mock_digraph_class): + """Test that view() is NOT called when show_view=False (default).""" + mock_digraph = MagicMock() + mock_digraph.render.return_value = "test.gv" + mock_digraph_class.return_value = mock_digraph + + write_graphviz(self.graph, "test", view=False) + + # Should NOT call view() when view=False + mock_digraph.view.assert_not_called() + mock_digraph.render.assert_called_once() + + @patch('hyperway.writer.graphviz.Digraph') + def test_write_graphviz_default_no_view(self, mock_digraph_class): + """Test that view() is NOT called by default.""" + mock_digraph = MagicMock() + mock_digraph.render.return_value = "test.gv" + mock_digraph_class.return_value = mock_digraph + + # Call without view parameter + write_graphviz(self.graph, "test") + + # Should NOT call view() by default + mock_digraph.view.assert_not_called() + + @patch('hyperway.writer.graphviz.Digraph') + def test_write_graphviz_with_format_parameter(self, mock_digraph_class): + """Test that format attribute is set when format parameter is provided.""" + mock_digraph = MagicMock() + mock_digraph.render.return_value = "test.gv" + mock_digraph_class.return_value = mock_digraph + + write_graphviz(self.graph, "test", format='svg') + + # Should set format attribute on the digraph + self.assertEqual(mock_digraph.format, 'svg') + mock_digraph.render.assert_called_once() + + @patch('hyperway.writer.graphviz.Digraph') + def test_write_graphviz_format_parameter_none(self, mock_digraph_class): + """Test that format attribute is NOT set when format=None.""" + mock_digraph = MagicMock() + mock_digraph.render.return_value = "test.gv" + mock_digraph_class.return_value = mock_digraph + + write_graphviz(self.graph, "test", format=None) + + # format attribute should not be set when format=None + # Check that format was never assigned after initialization + # (it's set in defaults but shouldn't be reassigned) + self.assertEqual(mock_digraph.render.call_count, 1) + + @patch('hyperway.writer.graphviz.Digraph') + def test_write_graphviz_with_directory_and_format(self, mock_digraph_class): + """Test rendering with both directory and format parameters.""" + mock_digraph = MagicMock() + mock_digraph.render.return_value = "output/test.svg" + mock_digraph_class.return_value = mock_digraph + + write_graphviz(self.graph, "test", directory="output", format='svg') + + # Should set format + self.assertEqual(mock_digraph.format, 'svg') + # Should pass directory to render + mock_digraph.render.assert_called_once_with(directory="output") + + @patch('hyperway.writer.graphviz.Digraph') + def test_write_graphviz_view_and_format_together(self, mock_digraph_class): + """Test that both view and format work together.""" + mock_digraph = MagicMock() + mock_digraph.render.return_value = "test.pdf" + mock_digraph_class.return_value = mock_digraph + + write_graphviz(self.graph, "test", view=True, format='pdf') + + # Should call view + mock_digraph.view.assert_called_once() + # Should set format + self.assertEqual(mock_digraph.format, 'pdf') + # Should render + mock_digraph.render.assert_called_once() + + @patch('hyperway.writer.graphviz.Digraph') + def test_write_graphviz_directory_none_not_passed(self, mock_digraph_class): + """Test that directory is not passed to render when None.""" + mock_digraph = MagicMock() + mock_digraph.render.return_value = "test.gv" + mock_digraph_class.return_value = mock_digraph + + write_graphviz(self.graph, "test") + + # Should call render without directory parameter + mock_digraph.render.assert_called_once_with() + + @patch('hyperway.writer.graphviz.Digraph') + def test_write_graphviz_all_options_combined(self, mock_digraph_class): + """Test all highlighted options working together.""" + mock_digraph = MagicMock() + mock_digraph.render.return_value = "renders/test.svg" + mock_digraph_class.return_value = mock_digraph + + result = write_graphviz( + self.graph, + "test_all_options", + view=True, + format='svg', + directory="renders" + ) + + # Should call view + mock_digraph.view.assert_called_once() + # Should set format + self.assertEqual(mock_digraph.format, 'svg') + # Should pass directory to render + mock_digraph.render.assert_called_once_with(directory="renders") + # Should return the digraph + self.assertEqual(result, mock_digraph) + class TestWriteGraphvizWithoutGraphviz(unittest.TestCase): """Test write_graphviz behavior when graphviz is unavailable.""" diff --git a/workspace/order-of-operation.py b/workspace/order-of-operation.py index 7ed652c..21f719e 100755 --- a/workspace/order-of-operation.py +++ b/workspace/order-of-operation.py @@ -23,6 +23,7 @@ c = make_edge(f.add_1, f.add_2, through=wire(f.mul_2)) -assert c.pluck(1) == 10 # (1 + 1) * 2 + 2 == 6 +r = c.pluck(1) +assert r == 6, f"Result was: {r}" # (1 + 1) * 2 + 2 == 6 assert c.pluck(10) == 24 # (10 + 1) * 2 + 2 == 24 diff --git a/workspace/run_2.gv b/workspace/run_2.gv index b98c4a0..f2f9f16 100644 --- a/workspace/run_2.gv +++ b/workspace/run_2.gv @@ -1,25 +1,25 @@ digraph run_2 { graph [rankdir=TB] node [arrowsize=0.8 color="#2299FF" fontcolor="#DDD" fontname=Arial shape=box style=rounded] - 139581092402896 [label="P_add_2.0"] - 139581092402944 [label="P_add_3.0"] - 139581092402992 [label="P_add_4.0"] - 139581092402416 [label="P_add_7.0"] - 139581092402464 [label="P_add_6.0"] - 139581092402512 [label="P_add_5.0"] - 139581092402368 [label="P_add_8.0"] - 139581092402320 [label="P_add_9.0"] - 139581092403040 [label="P_add_5.0"] - 139581092402848 [label="P_add_1.0"] - 139581092402944 -> 139581092402992 - 139581092402320 -> 139581092402368 - 139581092402848 -> 139581092402896 - 139581092402416 -> 139581092402848 - 139581092402368 -> 139581092402416 - 139581092402416 -> 139581092402464 - 139581092402992 -> 139581092403040 - 139581092402896 -> 139581092402944 - 139581092402464 -> 139581092402512 + 128109871881952 [label="P_add_4.0"] + 128109871881424 [label="P_add_9.0"] + 128109871881472 [label="P_add_8.0"] + 128109871881904 [label="P_add_3.0"] + 128109871881808 [label="P_add_1.0"] + 128109871882000 [label="P_add_5.0"] + 128109871881520 [label="P_add_7.0"] + 128109871881616 [label="P_add_5.0"] + 128109871881568 [label="P_add_6.0"] + 128109871881856 [label="P_add_2.0"] + 128109871881520 -> 128109871881808 + 128109871881952 -> 128109871882000 + 128109871881568 -> 128109871881616 + 128109871881808 -> 128109871881856 + 128109871881856 -> 128109871881904 + 128109871881472 -> 128109871881520 + 128109871881904 -> 128109871881952 + 128109871881424 -> 128109871881472 + 128109871881520 -> 128109871881568 overlap=false fontsize=12 bgcolor="#00000000" diff --git a/workspace/run_2.gv.png b/workspace/run_2.gv.png index d756b93d907fb690486ad189e8b9b96363b79b27..1d6620bc42b14c40a6135386757d19bef2691909 100644 GIT binary patch literal 27309 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 literal 27225 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�APV{Hwvp7mGt^*;Wz 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 MP+G|HTjrtv2hh499RL6T From 6f788e5bda268a149caf1570b79d39d96095886e Mon Sep 17 00:00:00 2001 From: Jay Date: Thu, 30 Oct 2025 22:01:33 +0000 Subject: [PATCH 2/2] Bump project version to 1.0.5 in pyproject.toml and sonar-project.properties --- pyproject.toml | 2 +- sonar-project.properties | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 8c008d1..526c4ac 100755 --- a/pyproject.toml +++ b/pyproject.toml @@ -5,7 +5,7 @@ build-backend = "setuptools.build_meta" [project] name = "hyperway" -version = "1.0.4" +version = "1.0.5" authors = [ { name="Strangemother", email="hyperway@strangemother.com" }, ] diff --git a/sonar-project.properties b/sonar-project.properties index 2e3c534..7330dfa 100644 --- a/sonar-project.properties +++ b/sonar-project.properties @@ -5,7 +5,7 @@ sonar.projectKey=Strangemother_python-hyperway sonar.organization=strangemother sonar.projectName=Python Hyperway -sonar.projectVersion=1.0.4 +sonar.projectVersion=1.0.5 # Source code settings sonar.sources=src/hyperway