From 1aeb4decaed1120b2347f034c23d187aa4823fe7 Mon Sep 17 00:00:00 2001 From: "Teal, D" Date: Wed, 14 Dec 2022 11:37:15 -0800 Subject: [PATCH 01/24] Adding example code block to doc string. --- porchlight/door.py | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/porchlight/door.py b/porchlight/door.py index 5824efb..ca73ffd 100644 --- a/porchlight/door.py +++ b/porchlight/door.py @@ -55,11 +55,22 @@ class BaseDoor: name : :py:obj:`str` The name of the function as visible from the base function's __name__. - return_types : :py:obj:`list` of :py:obj:`list` of `~typing.Type` + return_types : :py:obj:`dict` of :py:obj:`str`, :py:obj:`Type` pairs. Values returned by any return statements in the base function. - return_vals : :py:obj:`list` of :py:obj:`list` of :py:obj:`str` - Values returned by any return statements in the base function. + return_vals : :py:obj:`list` of :py:obj:`str` + Names of parameters returned by the base function. Any return + statements in a Door much haveidentical return parameters. I.e., the + following would fail if imported as a Door. + + .. code-block:: python + + def fxn(x): + if x < 1: + y = x + 1 + return x, y + + return x typecheck : :py:obj:`bool` If True, when arguments are passed to the `BaseDoor`'s base function @@ -78,7 +89,7 @@ class BaseDoor: min_n_return: int n_args: int name: str - return_types: List[List[Type]] + return_types: List[Type] return_vals: List[List[str]] typecheck: bool @@ -156,6 +167,8 @@ def _inspect_base_callable(self): self.keyword_args = {} self.keyword_only_args = {} + # Attempting to retrieve type hints for the return value. This *does + # not* fail if they aren't found. try: ret_type_annotation = typing.get_type_hints(function)["return"] self.return_types = decompose_type( From 447fc0441c81a23f1bd5560cf766e7c38291bdfd Mon Sep 17 00:00:00 2001 From: "Teal, D" Date: Wed, 14 Dec 2022 11:50:28 -0800 Subject: [PATCH 02/24] BaseDoor.return_vals is a list of str This specific commit fails unit tests. --- porchlight/door.py | 28 ++++++++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/porchlight/door.py b/porchlight/door.py index ca73ffd..79964a5 100644 --- a/porchlight/door.py +++ b/porchlight/door.py @@ -90,7 +90,7 @@ def fxn(x): n_args: int name: str return_types: List[Type] - return_vals: List[List[str]] + return_vals: List[str] typecheck: bool def __init__( @@ -247,7 +247,31 @@ def _inspect_base_callable(self): self.n_args = len(self.arguments) # The return values require some more effort. - self.return_vals = self._get_return_vals(function) + return_vals = self._get_return_vals(function) + + # porchlight >=v0.5.0 requires that return_vals be a single, non-nested + # list of return values that are uniform across return statements. + for i, ret_list in enumerate(return_vals): + if any(ret_list != rl for rl in return_vals): + msg = ( + f"Door objects do not allow for multiple return sets " + f"within the same function. That is, a function must " + f"always return the same set of parameters. But, " + f"{function.__name__} has return values:\n" + ) + + for i, rl in enumerate(return_vals): + msg += f" {i}) {', '.join(rl)}" + + logging.error(msg) + + raise DoorError(msg) + + if return_vals: + self.return_vals = return_vals[0] + + else: + self.return_vals = return_vals logger.debug(f"Found {self.n_args} arguments in {self.name}.") From b8f906df0c1df6db06fd8a129ce236a295e63cec Mon Sep 17 00:00:00 2001 From: "Teal, D" Date: Fri, 16 Dec 2022 11:35:17 -0800 Subject: [PATCH 03/24] Updating return value handling This commit updates the `return_vals` `BaseDoor` attr to be a list, and for all `BaseDoor` objects to assert that return instances are unique across all potential function returns. This guarantee does not cover automatic functions, and it may be prudent to add a warning for that in the future if return values don't map clearly to expectations. --- porchlight/door.py | 20 ++++++-------- porchlight/neighborhood.py | 7 ++--- porchlight/tests/test_basedoor.py | 11 ++++---- porchlight/tests/test_door.py | 40 +++++++++------------------ porchlight/tests/test_dynamicdoor.py | 2 +- porchlight/tests/test_neighborhood.py | 6 +++- 6 files changed, 36 insertions(+), 50 deletions(-) diff --git a/porchlight/door.py b/porchlight/door.py index 79964a5..0b28971 100644 --- a/porchlight/door.py +++ b/porchlight/door.py @@ -679,10 +679,10 @@ def map_arguments(self): del self.keyword_args[old_name] # Also change outputs that contain the same name. - for i, ret_tuple in enumerate(self.return_vals): - for j, ret_val in enumerate(ret_tuple): - if old_name == ret_val: - self.return_vals[i][j] = mapped_name + ret_tuple = self.return_vals + for i, ret_val in enumerate(ret_tuple): + if old_name == ret_val: + self.return_vals[i] = mapped_name # Place back in the original order. rev_argmap = {v: k for k, v in self.argmap.items()} @@ -747,10 +747,9 @@ def original_return_vals(self): return_vals = copy.copy(self.return_vals) # Also change outputs that contain the same name. - for i, ret_tuple in enumerate(self.return_vals): - for j, ret_val in enumerate(ret_tuple): - if ret_val in self.argmap: - return_vals[i][j] = self.argmap[ret_val] + for i, ret_val in enumerate(return_vals): + if ret_val in self.argmap: + return_vals[i] = self.argmap[ret_val] return return_vals @@ -775,9 +774,8 @@ def variables(self) -> List[str]: all_vars.append(arg) for ret in self.return_vals: - for r in ret: - if r not in all_vars: - all_vars.append(r) + if ret not in all_vars: + all_vars.append(ret) return all_vars diff --git a/porchlight/neighborhood.py b/porchlight/neighborhood.py index 5ab922b..356937e 100644 --- a/porchlight/neighborhood.py +++ b/porchlight/neighborhood.py @@ -53,7 +53,6 @@ def __init__(self, initial_doors: List[Callable] = []): else: self.add_function(d) - def __repr__(self): """Must communicate teh following: + A unique identifier. @@ -115,9 +114,9 @@ def add_door( :class:`~porchlight.door.DynamicDoor`, or :py:obj:`list` of :class:`~porchlight.door.Door` objects. - Either a single initialized - `door.Door` object or a list of them. If a list is provided, this - function is called for each item in the list. + Either a single initialized `door.Door` object or a list of them. + If a list is provided, this function is called for each item in the + list. overwrite_defaults : bool, optional If `True`, will overwrite any parameters shared between the diff --git a/porchlight/tests/test_basedoor.py b/porchlight/tests/test_basedoor.py index 06f26ec..0f4b6f3 100644 --- a/porchlight/tests/test_basedoor.py +++ b/porchlight/tests/test_basedoor.py @@ -24,7 +24,7 @@ def test_fxn(x: int) -> int: # Must contain both input and output parameter. arguments = ["x"] keyword_args = ["x"] - return_vals = [["y"]] + return_vals = ["y"] # Not comparing any values during this test. for arg in arguments: @@ -70,7 +70,7 @@ def test_fxn(x: int) -> int: # Must contain both input and output parameter. arguments = ["x"] keyword_args = ["x"] - return_vals = [["y"]] + return_vals = ["y"] # Not comparing any values during this test. for arg in arguments: @@ -79,8 +79,7 @@ def test_fxn(x: int) -> int: for kwarg in keyword_args: self.assertIn(kwarg, door.keyword_args) - for retval in return_vals: - self.assertIn(retval, door.return_vals) + self.assertEqual(return_vals, door.return_vals) # Call the BaseDoor result = door(x=5) @@ -93,7 +92,7 @@ def test_numpy_ufunc(self): except ModuleNotFoundError as e: # Printing a message and returning - print( + logging.error( f"NOTICE: Could not run test {self.id()}, got " f"ModuleNotFoundError: {e}." ) @@ -311,7 +310,7 @@ def test(x: int) -> int: expected_repr = ( f"BaseDoor(name=test, base_function={str(test)}, " f"arguments={{'x': }}, " - f"return_vals=[['y']])" + f"return_vals=['y'])" ) test_door = BaseDoor(test) diff --git a/porchlight/tests/test_door.py b/porchlight/tests/test_door.py index 99bef27..d343676 100644 --- a/porchlight/tests/test_door.py +++ b/porchlight/tests/test_door.py @@ -27,7 +27,7 @@ def test_fxn(x: int) -> int: # Must contain both input and output parameter. arguments = ["x"] keyword_args = ["x"] - return_vals = [["y"]] + return_vals = ["y"] # Not comparing any values during this test. for arg in arguments: @@ -36,8 +36,7 @@ def test_fxn(x: int) -> int: for kwarg in keyword_args: self.assertIn(kwarg, door.keyword_args) - for retval in return_vals: - self.assertIn(retval, door.return_vals) + self.assertEqual(return_vals, door.return_vals) # Call the Door result = door(x=5) @@ -158,7 +157,7 @@ def test(x: int) -> int: expected_repr = ( f"Door(name=test, base_function={str(test)}, " f"arguments={{'x': }}, " - f"return_vals=[['y']])" + f"return_vals=['y'])" ) test_door = Door(test) @@ -178,7 +177,7 @@ def orig_func(x, y: int = 1): return z, x self.assertEqual(my_func.variables, ["hello", "world", "z"]) - self.assertEqual(my_func.return_vals, [["z", "hello"]]) + self.assertEqual(my_func.return_vals, ["z", "hello"]) self.assertEqual(my_func.required_arguments, ["hello"]) self.assertEqual(my_func.original_arguments, orig_func.arguments) self.assertEqual(my_func.original_return_vals, orig_func.return_vals) @@ -206,7 +205,7 @@ def test2_unmapped( test2_mapped.variables, ["temperature", "pressure", "k_B", "density"], ) - self.assertEqual(test2_mapped.return_vals, [["density"]]) + self.assertEqual(test2_mapped.return_vals, ["density"]) self.assertEqual(test2_mapped.required_arguments, []) self.assertEqual( test2_mapped.original_arguments, test2_unmapped.arguments @@ -231,30 +230,17 @@ def test4(x): self.assertEqual(list(test4.arguments.keys()), ["x"]) def test_mapping_multiple_returns(self): - @Door(argument_mapping={"hello": "x", "bing": "z"}) - def test1(x, y: int, z=1) -> typing.Tuple[int]: - result = True if x < y else False + with self.assertRaises(DoorError): - if result: - return result, x, z + @Door(argument_mapping={"hello": "x", "bing": "z"}) + def test1(x, y: int, z=1) -> typing.Tuple[int]: + result = True if x < y else False - else: - return result, y, z + if result: + return result, x, z - self.maxDiff = None - self.assertEqual( - test1.return_vals, - [["result", "hello", "bing"], ["result", "y", "bing"]], - ) - self.assertEqual(list(test1.arguments.keys()), ["hello", "y", "bing"]) - self.assertEqual( - test1.keyword_arguments, - { - "hello": Param("hello"), - "y": Param("y"), - "bing": Param("bing", 1), - }, - ) + else: + return result, y, z def test_bad_mapping_variable_names(self): bad_names = ("0fign_", " bonk", "this is a sentence...", "there.dot") diff --git a/porchlight/tests/test_dynamicdoor.py b/porchlight/tests/test_dynamicdoor.py index d046852..c6c2a14 100644 --- a/porchlight/tests/test_dynamicdoor.py +++ b/porchlight/tests/test_dynamicdoor.py @@ -95,7 +95,7 @@ def my_door(x: int, y: int = 1) -> int: self.assertEqual(doorgen1(2, 5), 2 ** 5) self.assertEqual(doorgen1.arguments, {"hello": int, "y": int}) - self.assertEqual(doorgen1.return_vals, [["z"]]) + self.assertEqual(doorgen1.return_vals, ["z"]) @door.Door(argument_mapping={"hello": "x"}) def doorgen2(x: int, y: int = 1) -> door.Door: diff --git a/porchlight/tests/test_neighborhood.py b/porchlight/tests/test_neighborhood.py index 612a77c..e400f09 100644 --- a/porchlight/tests/test_neighborhood.py +++ b/porchlight/tests/test_neighborhood.py @@ -31,7 +31,7 @@ def test2(y: int) -> int: neighborhood = Neighborhood([test1, test2]) - self.assertEqual(list(neighborhood.params.keys()), ['x', 'y', 'z']) + self.assertEqual(list(neighborhood.params.keys()), ["x", "y", "z"]) def test___repr__(self): neighborhood = Neighborhood() @@ -176,6 +176,10 @@ def test4(): neighborhood.add_door([test1, test2, test3, test4]) + import pytest + + pytest.set_trace() + self.assertEqual(len(neighborhood._doors), 4) self.assertEqual(len(neighborhood._params), 4) From 5b485244edbe1530729fa24da553734f897acc31 Mon Sep 17 00:00:00 2001 From: "Teal, D" Date: Fri, 16 Dec 2022 12:33:59 -0800 Subject: [PATCH 04/24] Passing tests, fixed lurking references There were a few remaining references to `BaseDoor.return_vals[0]` that worked in the previous versions. Also adjusted some comments. --- porchlight/neighborhood.py | 25 ++++++++----------------- porchlight/tests/test_neighborhood.py | 4 ---- 2 files changed, 8 insertions(+), 21 deletions(-) diff --git a/porchlight/neighborhood.py b/porchlight/neighborhood.py index 356937e..3143fe7 100644 --- a/porchlight/neighborhood.py +++ b/porchlight/neighborhood.py @@ -163,17 +163,16 @@ def add_door( # Add all return values as parameters. if not dynamic_door: - for pname in [p for rvs in new_door.return_vals for p in rvs]: + for pname in new_door.return_vals: if pname not in self._params: self._params[pname] = param.Param(pname, param.Empty()) return # Dynamic doors must be specified separately. They get initialized when - # first modified. + # first called/explicitly generated. # - # Dynamic doors must also be type-annotated. If they are not raise an - # error. + # Dynamic doors must also be type-annotated. if ( "return_types" not in new_door.__dict__ or not new_door.return_types @@ -190,7 +189,7 @@ def add_door( for i, rt in enumerate(return_types): if isinstance(rt, door.Door) or rt is door.Door: - ret_val = new_door.return_vals[0][i] + ret_val = new_door.return_vals[i] if ret_val not in self.doors: # Can define a function that just pulls out the attr # independent of its current reference. @@ -391,19 +390,13 @@ def call_all_doors(self): if not cur_door.return_vals: continue - elif len(cur_door.return_vals[0]) > 1: - # This only works for functions with one possible output. This, - # frankly, should probably be the case nearly all of the time. - # Still need to make a call on if there's support in a subset - # of cases. See issue #19 for updates. + elif len(cur_door.return_vals) > 1: update_params = { - v: x for v, x in zip(cur_door.return_vals[0], output) + v: x for v, x in zip(cur_door.return_vals, output) } else: - assertmsg = "Mismatched output/return." - assert len(cur_door.return_vals) == 1, assertmsg - update_params = {cur_door.return_vals[0][0]: output} + update_params = {cur_door.return_vals[0]: output} for pname, new_value in update_params.items(): # If the parameter is currently empty, just reassign and @@ -421,9 +414,7 @@ def call_all_doors(self): logger.error(msg) raise param.ParameterError(msg) - # Editing the _value directly here... but I'm not sure if - # that's the best idea. - self._params[pname]._value = new_value + self._params[pname].value = new_value def run_step(self): """Runs a single step forward for all functions, in specified order, diff --git a/porchlight/tests/test_neighborhood.py b/porchlight/tests/test_neighborhood.py index e400f09..1178a46 100644 --- a/porchlight/tests/test_neighborhood.py +++ b/porchlight/tests/test_neighborhood.py @@ -176,10 +176,6 @@ def test4(): neighborhood.add_door([test1, test2, test3, test4]) - import pytest - - pytest.set_trace() - self.assertEqual(len(neighborhood._doors), 4) self.assertEqual(len(neighborhood._params), 4) From 2021be926c71ab45e98be05e3ba7157893eebced Mon Sep 17 00:00:00 2001 From: "Teal, D" Date: Mon, 19 Dec 2022 12:19:18 -0800 Subject: [PATCH 05/24] yield statements now supported Changed one regex statement to include `yield` alongside `return` as an option. Also added a test to account for a yield statement, though this test isn't particularly thorough. Given to `porchlight`, this really is just a change in return vs. yield (since the user will still need to set this up so `next` is called on the generator). A future update could include more explicit generator support, but I'm not sure that's feasible for 1.0. --- porchlight/door.py | 1 + porchlight/tests/test_basedoor.py | 17 +++++++++++++++++ 2 files changed, 18 insertions(+) diff --git a/porchlight/door.py b/porchlight/door.py index 0b28971..b476f80 100644 --- a/porchlight/door.py +++ b/porchlight/door.py @@ -365,6 +365,7 @@ def _get_return_vals(function: Callable) -> List[str]: # definition. defmatch_str = r"^(\ )+def\s+" retmatch_str = r".*\s+return\s(.*)" + retmatch_str = r"^\s+(?:return|yield)\s(.*)" indentmatch_str = r"^(\s)*" for i, line in enumerate(lines): diff --git a/porchlight/tests/test_basedoor.py b/porchlight/tests/test_basedoor.py index 0f4b6f3..a4508ad 100644 --- a/porchlight/tests/test_basedoor.py +++ b/porchlight/tests/test_basedoor.py @@ -391,6 +391,23 @@ def test_prop(arg1, arg2, kwarg1=None, kwarg2=["hi"]) -> int: self.assertEqual(test_prop.kwargs, expected_val) + def test_generator(self): + # Tests generator functions with porchlight. + @BaseDoor + def testgen1(x): + y = 0 + + while y <= x: + yield y + y = y + 1 + + return y + + expected = {"arguments": {"x": Empty()}, "return_vals": ["y"]} + + for attr, val in expected.items(): + self.assertEqual(getattr(testgen1, attr), val) + if __name__ == "__main__": import unittest From b6bd7573a965e87835209ebca04d3a7427d9fc49 Mon Sep 17 00:00:00 2001 From: "Teal, D" Date: Mon, 19 Dec 2022 12:24:30 -0800 Subject: [PATCH 06/24] Extra test + removing unused BaseDoor attrs Removed, specifically, two class attrs from the original development days. This update officially removes them. --- porchlight/door.py | 8 -------- porchlight/tests/test_basedoor.py | 24 ++++++++++++++++++++++++ 2 files changed, 24 insertions(+), 8 deletions(-) diff --git a/porchlight/door.py b/porchlight/door.py index b476f80..415b5b9 100644 --- a/porchlight/door.py +++ b/porchlight/door.py @@ -43,12 +43,6 @@ class BaseDoor: arguments without a default value are assigned a :class:~porchlight.param.Empty` value instead of their default value. - max_n_return : :py:obj:`int` - Maximum number of returned values. - - min_n_return : :py:obj:`int` - Minimum number of returned values. - n_args : :py:obj:`int` Number of arguments accepted by this `BaseDoor` @@ -85,8 +79,6 @@ def fxn(x): _base_function: Callable arguments: Dict[str, Type] keyword_args: Dict[str, Any] - max_n_return: int - min_n_return: int n_args: int name: str return_types: List[Type] diff --git a/porchlight/tests/test_basedoor.py b/porchlight/tests/test_basedoor.py index a4508ad..8590408 100644 --- a/porchlight/tests/test_basedoor.py +++ b/porchlight/tests/test_basedoor.py @@ -408,6 +408,30 @@ def testgen1(x): for attr, val in expected.items(): self.assertEqual(getattr(testgen1, attr), val) + # Should be identical to something with return instead of yield (for + # porchlight specifically). + @BaseDoor + def test1(x): + y = 0 + + while y <= x: + return y + y = y + 1 + + return y + + test_attrs = [ + "arguments", + "positional_only", + "keyword_args", + "n_args", + "return_types", + "return_vals", + ] + + for attr in test_attrs: + self.assertEqual(getattr(test1, attr), getattr(testgen1, attr)) + if __name__ == "__main__": import unittest From 558cb203c6561146dbf9e6db70f591b3b9279d66 Mon Sep 17 00:00:00 2001 From: "Teal, D" Date: Thu, 22 Dec 2022 12:13:46 -0800 Subject: [PATCH 07/24] Changes param.Empty to be a singleton Previously, param.Empty could be instantiated or used as a class for comparison. This commit updates the class to have only one instance at any given time. This commit also updates tests and places elsewhere in the package relying on the old `Empty == Empty()` standard. --- porchlight/door.py | 6 +++--- porchlight/neighborhood.py | 3 +-- porchlight/param.py | 19 ++++++++++++------- porchlight/tests/test_basedoor.py | 4 ++-- porchlight/tests/test_door.py | 6 ++++-- porchlight/tests/test_neighborhood.py | 20 ++++++++++---------- 6 files changed, 32 insertions(+), 26 deletions(-) diff --git a/porchlight/door.py b/porchlight/door.py index 5824efb..9565265 100644 --- a/porchlight/door.py +++ b/porchlight/door.py @@ -229,7 +229,7 @@ def _inspect_base_callable(self): for name, _type in self.arguments.items(): if _type == inspect._empty: - self.arguments[name] = Empty + self.arguments[name] = Empty() self.n_args = len(self.arguments) @@ -262,7 +262,7 @@ def __call__(self, *args, **kwargs): # Type checking. if self.typecheck: for k, v in input_kwargs.items(): - if self.arguments[k] == Empty: + if self.arguments[k] == Empty(): continue if not isinstance(v, self.arguments[k]): @@ -750,7 +750,7 @@ def required_arguments(self) -> List[str]: required = [] for x in self.arguments: - if isinstance(self.keyword_args[x].value, Empty): + if self.keyword_args[x].value == Empty(): required.append(x) return required diff --git a/porchlight/neighborhood.py b/porchlight/neighborhood.py index 5ab922b..4c696d7 100644 --- a/porchlight/neighborhood.py +++ b/porchlight/neighborhood.py @@ -53,7 +53,6 @@ def __init__(self, initial_doors: List[Callable] = []): else: self.add_function(d) - def __repr__(self): """Must communicate teh following: + A unique identifier. @@ -206,7 +205,7 @@ def template_ddoor(param_name): template_ddoor.generator_kwargs = {"param_name": ret_val} self.add_door(template_ddoor) - self.add_param(ret_val, param.Empty) + self.add_param(ret_val, param.Empty()) def remove_door(self, name: str): """Removes a :class:`~porchlight.door.Door` from :attr:`_doors`. diff --git a/porchlight/param.py b/porchlight/param.py index 7908c1c..4cdc397 100644 --- a/porchlight/param.py +++ b/porchlight/param.py @@ -12,16 +12,21 @@ class ParameterError(Exception): class Empty: - """An empty class representing missing parameters values.""" + """An empty class representing missing parameters values. - def __init__(self): - pass + At initializtion, if an instance does not already exist it is created. + """ + + def __new__(cls): + # Return the singleton instance if it exists already, otherwise become + # the singleton instance. + if not hasattr(cls, "_singleton_instance"): + cls._singleton_instance = super(Empty, cls).__new__(cls) + + return cls._singleton_instance def __eq__(self, other): - """Force Equality of this special value regardless of whether it is - initialized or not. - """ - if isinstance(other, Empty) or other == Empty: + if other is self: return True else: diff --git a/porchlight/tests/test_basedoor.py b/porchlight/tests/test_basedoor.py index 06f26ec..8f330eb 100644 --- a/porchlight/tests/test_basedoor.py +++ b/porchlight/tests/test_basedoor.py @@ -51,7 +51,7 @@ def fxn_use_decorator(x): self.assertEqual(fxn_use_decorator.name, "fxn_use_decorator") - self.assertEqual(fxn_use_decorator.arguments, {"x": Empty}) + self.assertEqual(fxn_use_decorator.arguments, {"x": Empty()}) # Test on a decorated function. def test_decorator(fxn): @@ -339,7 +339,7 @@ def test1( "pos2": Empty(), "kwpos": Empty(), "kwposdef": Empty(), - "kwonly": Empty, + "kwonly": Empty(), } self.assertEqual(new_door.arguments, expected_arguments) diff --git a/porchlight/tests/test_door.py b/porchlight/tests/test_door.py index 99bef27..844bff8 100644 --- a/porchlight/tests/test_door.py +++ b/porchlight/tests/test_door.py @@ -54,7 +54,7 @@ def fxn_use_decorator(x): self.assertEqual(fxn_use_decorator.name, "fxn_use_decorator") - self.assertEqual(fxn_use_decorator.arguments, {"x": Empty}) + self.assertEqual(fxn_use_decorator.arguments, {"x": Empty()}) def test___call__(self): def test_fxn(x: int) -> int: @@ -281,7 +281,9 @@ def test1(x, y: int, z=1) -> int: test2 = Door(test1) - self.assertEqual(test2.arguments, {"x": Empty, "y": int, "z": Empty}) + self.assertEqual( + test2.arguments, {"x": Empty(), "y": int, "z": Empty()} + ) def test_bad_mapping_bad_functions(self): # A bad argument diff --git a/porchlight/tests/test_neighborhood.py b/porchlight/tests/test_neighborhood.py index 612a77c..e454fd2 100644 --- a/porchlight/tests/test_neighborhood.py +++ b/porchlight/tests/test_neighborhood.py @@ -31,7 +31,7 @@ def test2(y: int) -> int: neighborhood = Neighborhood([test1, test2]) - self.assertEqual(list(neighborhood.params.keys()), ['x', 'y', 'z']) + self.assertEqual(list(neighborhood.params.keys()), ["x", "y", "z"]) def test___repr__(self): neighborhood = Neighborhood() @@ -48,15 +48,15 @@ def test1(x: int) -> int: # Keeping the below as an example of what the string looks like. Update # this to match the actual implementation as changes occur below. # expected = ( - # "Neighborhood(doors={'test1': Door(name=test1, " - # "base_function=, " - # "arguments={'x': }, return_vals=[['y']])}, " - # "params={'x': Param(name=x, value=, constant=False, type=), 'y': Param(name=" - # "y, value=, " - # "constant=False, type=)}, call_order=['test1'])" + # Neighborhood(doors={'test1': Door(name=test1, + # base_function=.test1 at 0x7fdcf1333640>, arguments={'x': }, + # return_vals=[['y']])}, params={'x': Para m(name=x, + # value=, + # constant=False, type=), 'y': + # Param(name=y, value=, constant= False, type=)}, call_order=['test1']) # ) params = neighborhood.params From 18e6c6a231e08c4f46ed6db1a6243950197d1fd8 Mon Sep 17 00:00:00 2001 From: "Teal, D" Date: Thu, 22 Dec 2022 12:17:09 -0800 Subject: [PATCH 08/24] Edit Empty tests to run valid tests. --- porchlight/tests/test_param.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/porchlight/tests/test_param.py b/porchlight/tests/test_param.py index a1dcc97..3f9a7f7 100644 --- a/porchlight/tests/test_param.py +++ b/porchlight/tests/test_param.py @@ -103,8 +103,7 @@ class TestEmpty(TestCase): def test___eq__(self): empty = param.Empty() - self.assertTrue(empty, param.Empty) - self.assertTrue(empty, param.Empty()) + self.assertEqual(empty, param.Empty()) self.assertNotEqual(empty, 1) From 6f6c7c415060deb101cd6fc1e965a310fd29a58e Mon Sep 17 00:00:00 2001 From: "Teal, D" Date: Thu, 22 Dec 2022 13:12:31 -0800 Subject: [PATCH 09/24] Removing print statement from test. --- porchlight/tests/test_neighborhood.py | 1 - 1 file changed, 1 deletion(-) diff --git a/porchlight/tests/test_neighborhood.py b/porchlight/tests/test_neighborhood.py index e454fd2..40e28b2 100644 --- a/porchlight/tests/test_neighborhood.py +++ b/porchlight/tests/test_neighborhood.py @@ -66,7 +66,6 @@ def test1(x: int) -> int: f"params={params}, call_order=['test1'])" ) - print(repr(neighborhood)) self.assertEqual(repr(neighborhood), expected) def test_add_function(self): From 4edb332aeb68efcd8780c304cbb0aab923c8a7ba Mon Sep 17 00:00:00 2001 From: "Teal, D" Date: Thu, 22 Dec 2022 14:08:12 -0800 Subject: [PATCH 10/24] Changes to logging and removing _dynamic_doors attr --- porchlight/neighborhood.py | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/porchlight/neighborhood.py b/porchlight/neighborhood.py index a88189a..e673e91 100644 --- a/porchlight/neighborhood.py +++ b/porchlight/neighborhood.py @@ -36,12 +36,10 @@ class Neighborhood: """ _doors: Dict[str, door.Door] - _dynamic_doors: set[str] _params: Dict[str, param.Param] _call_order: List[str] def __init__(self, initial_doors: List[Callable] = []): - """Initializes the Neighborhood object.""" self._doors = {} self._params = {} self._call_order = [] @@ -53,12 +51,18 @@ def __init__(self, initial_doors: List[Callable] = []): else: self.add_function(d) + # Logging + msg = ( + f"Neighborhood initialized with {len(self._doors)} " + f"doors/functions." + ) + logger.debug(msg) + def __repr__(self): - """Must communicate teh following: - + A unique identifier. - + List of doors - + list of tracked parameters and their values. - """ + # Must communicate the following: + # + A unique identifier. + # + List of doors + # + list of tracked parameters and their values. info = { "doors": self.doors, "params": self.params, @@ -97,6 +101,7 @@ def add_function( object. """ new_door = door.Door(function) + logging.debug(f"Adding new function to neighborhood: {new_door.name}") self.add_door(new_door, overwrite_defaults, dynamic_door) From 25b5e520f594d1d6ec73d67fb1a5b6ec0e172107 Mon Sep 17 00:00:00 2001 From: "Teal, D" Date: Thu, 22 Dec 2022 14:31:17 -0800 Subject: [PATCH 11/24] Updating logging, warnings, and tests. --- porchlight/__init__.py | 9 ++++---- porchlight/door.py | 30 ++++++++++++++++++++++++++- porchlight/tests/test_basedoor.py | 3 ++- porchlight/tests/test_neighborhood.py | 4 +++- 4 files changed, 39 insertions(+), 7 deletions(-) diff --git a/porchlight/__init__.py b/porchlight/__init__.py index 008e7b6..a562524 100644 --- a/porchlight/__init__.py +++ b/porchlight/__init__.py @@ -1,10 +1,11 @@ -# Initialize logging -import logging import os from .door import Door from .neighborhood import Neighborhood from .param import Param -logging.basicConfig(filename=f"{os.getcwd()}/porchlight.log") -loggers = logging.getLogger(__name__) + +# Initialize logging +import logging + +logging.getLogger(__name__).addHandler(logging.NullHandler()) diff --git a/porchlight/door.py b/porchlight/door.py index 3124721..9fdd099 100644 --- a/porchlight/door.py +++ b/porchlight/door.py @@ -15,6 +15,7 @@ import typing from typing import Any, Callable, Dict, List, Type +import warnings import logging logger = logging.getLogger(__name__) @@ -24,6 +25,10 @@ class DoorError(Exception): pass +class DoorWarning(Warning): + pass + + class BaseDoor: """Contains the basic information about a function such as expected arguments, type annotations, and named return values. @@ -414,13 +419,25 @@ def _get_return_vals(function: Callable) -> List[str]: vals = [v.strip() for v in vals] + # Checks to ensure it's not just a bunch of/one empty string, + # which just implies that the line is: + # return + # + # While this could be applied to vals, it could obfuscate the + # error that *must* occur in those cases, which is a + # SyntaxError. Trusting the parser here. + if not [v for v in vals if v != ""]: + # This is empty. + vals = [] + for val in vals: if not re.match(r"\w+$", val): # This is undefined, not an error. So assign return # value 'undefined' for this return statement and issue # a warning. source_file = inspect.getfile(function) - logger.warning( + + msg = ( f"Could not define any set of return variable " f"names for the following return line: \n" f"{source_file}: {start_line+i}) " @@ -430,6 +447,10 @@ def _get_return_vals(function: Callable) -> List[str]: f"callable." ) + logger.warning(msg) + + warnings.warn(msg, DoorWarning) + vals = [] break @@ -697,6 +718,8 @@ def _check_argmap(argmap): exception if it is invalid. Will also raise warnings for certain non-fatal actions. """ + builtin_list = dir(__builtins__) + for key, value in argmap.items(): # Argument map should contain valid python variable names. if not re.match(r"^[a-zA-Z_]([a-zA-Z0-9_])*$", key): @@ -704,6 +727,11 @@ def _check_argmap(argmap): logging.error(msg) raise DoorError(msg) + if key in builtin_list: + msg = f"Key {key} matches built-in name." + logger.warning(msg) + warnings.warn(msg, DoorWarning) + def __repr__(self): return super().__repr__().replace("BaseDoor", "Door") diff --git a/porchlight/tests/test_basedoor.py b/porchlight/tests/test_basedoor.py index d72910e..782c6f9 100644 --- a/porchlight/tests/test_basedoor.py +++ b/porchlight/tests/test_basedoor.py @@ -189,7 +189,8 @@ def test_oneline(x): def test_oneline_bad(x): return x * 2 - result = BaseDoor._get_return_vals(test_oneline_bad) + with self.assertWarns(door.DoorWarning): + result = BaseDoor._get_return_vals(test_oneline_bad) self.assertEqual(result, []) diff --git a/porchlight/tests/test_neighborhood.py b/porchlight/tests/test_neighborhood.py index 40e28b2..018e012 100644 --- a/porchlight/tests/test_neighborhood.py +++ b/porchlight/tests/test_neighborhood.py @@ -467,7 +467,9 @@ def fxn_two(y): neighborhood = Neighborhood() neighborhood.add_function(fxn_one) - neighborhood.add_function(fxn_two) + + with self.assertWarns(door.DoorWarning): + neighborhood.add_function(fxn_two) # Provide the required first arg, x neighborhood.set_param("x", 0) From 65cd66c184dd82b775fa41884eeb6e435a736880 Mon Sep 17 00:00:00 2001 From: "Teal, D" Date: Thu, 22 Dec 2022 14:38:04 -0800 Subject: [PATCH 12/24] Removing an Empty() check in door calls I believe it was there previously for a former implementation. --- porchlight/door.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/porchlight/door.py b/porchlight/door.py index 9fdd099..fa61870 100644 --- a/porchlight/door.py +++ b/porchlight/door.py @@ -296,9 +296,6 @@ def __call__(self, *args, **kwargs): # Type checking. if self.typecheck: for k, v in input_kwargs.items(): - if self.arguments[k] == Empty(): - continue - if not isinstance(v, self.arguments[k]): msg = ( f"Type checking is on, and the type for input " From 71f288479bc21684a8f8feea45afd3c2cc726246 Mon Sep 17 00:00:00 2001 From: "Teal, D" Date: Thu, 22 Dec 2022 14:44:18 -0800 Subject: [PATCH 13/24] User names will override inspected names. --- porchlight/door.py | 8 ++++++-- porchlight/tests/test_door.py | 8 ++++++++ 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/porchlight/door.py b/porchlight/door.py index fa61870..3affb04 100644 --- a/porchlight/door.py +++ b/porchlight/door.py @@ -157,8 +157,12 @@ def _inspect_base_callable(self): # arguments in the end. function = get_wrapped_function(self._base_function) - self.name = function.__name__ - self.__name__ = function.__name__ + # Name may be otherwise assigned, this is a safe way to ensure that + # does not get overwritten. + if not hasattr(self, "name") or not self.name: + self.name = function.__name__ + self.__name__ = function.__name__ + self.arguments = {} self.positional_only = [] self.keyword_args = {} diff --git a/porchlight/tests/test_door.py b/porchlight/tests/test_door.py index 905fd8f..25d7dba 100644 --- a/porchlight/tests/test_door.py +++ b/porchlight/tests/test_door.py @@ -55,6 +55,14 @@ def fxn_use_decorator(x): self.assertEqual(fxn_use_decorator.arguments, {"x": Empty()}) + # Try manually naming the door. + @Door(name="testname") + def test1(): + pass + + self.assertEqual(test1.__name__, "testname") + self.assertEqual(test1.name, "testname") + def test___call__(self): def test_fxn(x: int) -> int: y = 2 * x From 4b7c141b6eb4bd4bb00f389991d6688d7991f319 Mon Sep 17 00:00:00 2001 From: "Teal, D" Date: Thu, 22 Dec 2022 15:00:50 -0800 Subject: [PATCH 14/24] Adding test for decorated auto-wrapping. --- porchlight/tests/test_door.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/porchlight/tests/test_door.py b/porchlight/tests/test_door.py index 25d7dba..30947f1 100644 --- a/porchlight/tests/test_door.py +++ b/porchlight/tests/test_door.py @@ -397,6 +397,14 @@ def test1(x: int, *, y=0) -> int: self.assertEqual(normal_door(5), wrapped_door(5)) self.assertEqual(normal_door(-500, y=2), wrapped_door(-500, y=2)) + # Auto-wrapping cannot be used with decorators. + with self.assertRaises(DoorError): + + @Door(wrapped=True, **kwargs) + def test2(x: int, *, y=0) -> int: + z = x ** y + return z + if __name__ == "__main__": unittest.main() From f0afa2a3d9103cc943177a613ebae9d23ec79f99 Mon Sep 17 00:00:00 2001 From: "Teal, D" Date: Thu, 22 Dec 2022 15:14:59 -0800 Subject: [PATCH 15/24] Warnings for maps with builtin counterpart The code now throws a DoorWarning if an argument mapping passed to a Door object contains any values that are equivalent to any builtin command. E.g., "type" would raise a door warning. I see this frequently enough on the day-to-day that I think it's useful to warn. --- porchlight/door.py | 12 ++++++++++-- porchlight/tests/test_door.py | 15 ++++++++++++++- 2 files changed, 24 insertions(+), 3 deletions(-) diff --git a/porchlight/door.py b/porchlight/door.py index 3affb04..44fc241 100644 --- a/porchlight/door.py +++ b/porchlight/door.py @@ -163,6 +163,9 @@ def _inspect_base_callable(self): self.name = function.__name__ self.__name__ = function.__name__ + else: + logging.debug(f"Ignoring name assignment for {self.name}") + self.arguments = {} self.positional_only = [] self.keyword_args = {} @@ -719,7 +722,7 @@ def _check_argmap(argmap): exception if it is invalid. Will also raise warnings for certain non-fatal actions. """ - builtin_list = dir(__builtins__) + builtin_set = set(bi for bi in __builtins__.keys()) for key, value in argmap.items(): # Argument map should contain valid python variable names. @@ -728,11 +731,16 @@ def _check_argmap(argmap): logging.error(msg) raise DoorError(msg) - if key in builtin_list: + if key in builtin_set: msg = f"Key {key} matches built-in name." logger.warning(msg) warnings.warn(msg, DoorWarning) + if value in builtin_set: + msg = f"Mapping arg {value} matches global name." + logger.warning(msg) + warnings.warn(msg, DoorWarning) + def __repr__(self): return super().__repr__().replace("BaseDoor", "Door") diff --git a/porchlight/tests/test_door.py b/porchlight/tests/test_door.py index 30947f1..9428062 100644 --- a/porchlight/tests/test_door.py +++ b/porchlight/tests/test_door.py @@ -4,7 +4,7 @@ from unittest import TestCase import unittest -from porchlight.door import Door, DoorError +from porchlight.door import Door, DoorError, DoorWarning from porchlight.param import Empty, Param, ParameterError import typing @@ -310,6 +310,19 @@ def test_bad_mapping_name_err_and_warning(self): def test1(x, y): pass + def test_builtin_mapping_name_warning(self): + with self.assertWarns(DoorWarning): + + @Door(argument_mapping={"type": "x"}) + def test1(x): + pass + + with self.assertWarns(DoorWarning): + + @Door(argument_mapping={"hola": "type"}) + def test2(type): + return + def test_argument_mapping_property(self): @Door(argument_mapping={"hello": "x", "world": "z"}) def test1(x, y: int, z=1) -> int: From 797f86ca8fa8f1a0e9264c170520e62ef149a67f Mon Sep 17 00:00:00 2001 From: "Teal, D" Date: Thu, 22 Dec 2022 15:19:52 -0800 Subject: [PATCH 16/24] Coverage increased for utils.inspect_functions. Now it's 100%. --- porchlight/tests/test_utils_inspect_functions.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/porchlight/tests/test_utils_inspect_functions.py b/porchlight/tests/test_utils_inspect_functions.py index 6ac3de4..e92bb9d 100644 --- a/porchlight/tests/test_utils_inspect_functions.py +++ b/porchlight/tests/test_utils_inspect_functions.py @@ -154,6 +154,16 @@ def test4_2(): self.assertEqual(result[0], expected_result) + def test_non_callable(self): + tests = [1, "2", {3: 4}] + + for test in tests: + with self.assertRaises(TypeError): + inspect_functions.get_all_source(test) + + with self.assertRaises(TypeError): + inspect_functions.get_wrapped_function(test) + if __name__ == "__main__": unittest.main() From 2c506705158877913f2dfe7e9573b7b030062163 Mon Sep 17 00:00:00 2001 From: "Teal, D" Date: Thu, 22 Dec 2022 15:29:35 -0800 Subject: [PATCH 17/24] Minor chances to decompose-type --- porchlight/utils/typing_functions.py | 1 - 1 file changed, 1 deletion(-) diff --git a/porchlight/utils/typing_functions.py b/porchlight/utils/typing_functions.py index 0a6dd6d..2ce380e 100644 --- a/porchlight/utils/typing_functions.py +++ b/porchlight/utils/typing_functions.py @@ -28,7 +28,6 @@ def decompose_type( are ignored. These are only for *resolvable* types at return, such as Tuples, Lists, and Iterables. Callables are excluded by default. """ - all_types = [] # Check that typevar is not one of interest to any of the types in From 837e65c3613b92fb10895f30048f6abba5930b38 Mon Sep 17 00:00:00 2001 From: "Teal, D" Date: Thu, 22 Dec 2022 15:37:58 -0800 Subject: [PATCH 18/24] Adding more debug logging statements. --- porchlight/door.py | 2 ++ porchlight/neighborhood.py | 12 +++++++++++- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/porchlight/door.py b/porchlight/door.py index 44fc241..3bf521f 100644 --- a/porchlight/door.py +++ b/porchlight/door.py @@ -125,6 +125,8 @@ def __init__( self.typecheck = typecheck self._inspect_base_callable() + logging.debug(f"Door {self.name} initialized.") + def __eq__(self, other) -> bool: """Equality is defined as referencing the same base function.""" if isinstance(other, BaseDoor) and self.name is other.name: diff --git a/porchlight/neighborhood.py b/porchlight/neighborhood.py index e673e91..5017195 100644 --- a/porchlight/neighborhood.py +++ b/porchlight/neighborhood.py @@ -389,10 +389,12 @@ def call_all_doors(self): input_params[pname] = self._params[pname].value # Run the cur_door object and catch its output. + logging.debug(f"Calling door {cur_door.name}.") output = cur_door(**input_params) # Check if the cur_door has a known return value. if not cur_door.return_vals: + logging.debug("No return value found.") continue elif len(cur_door.return_vals) > 1: @@ -403,6 +405,9 @@ def call_all_doors(self): else: update_params = {cur_door.return_vals[0]: output} + logging.debug(f"Updating parameters: {list(update_params.keys())}") + + # Update all parameters to reflect the next values. for pname, new_value in update_params.items(): # If the parameter is currently empty, just reassign and # continue. This refreshes the type value of the parameter @@ -455,12 +460,15 @@ def order_doors(self, order: List[str]): The order for doors to be called in. Each `str` must correspond to a key in `Neighborhood._doors`. """ + # The order list must: + # + Contain all doors at least once. + # + All doors must already exist and be known. if not order: msg = f"Empty or invalid input: {order}." logger.error(msg) raise ValueError(msg) - elif len(order) > len(self._doors): + elif len(set(order)) > len(self._doors): msg = ( f"More labels provided than doors " f"({len(order)} labels vs {len(self._doors)} doors)." @@ -475,6 +483,8 @@ def order_doors(self, order: List[str]): logger.error(msg) raise KeyError(msg) + logging.debug(f"Adjusting call order: {self._call_order} -> {order}") + self._call_order = order @property From b140b74c4547dba0464f06ee175f6a556ecad448 Mon Sep 17 00:00:00 2001 From: "Teal, D" Date: Thu, 22 Dec 2022 15:42:38 -0800 Subject: [PATCH 19/24] Updating reordering doors to be as before --- porchlight/neighborhood.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/porchlight/neighborhood.py b/porchlight/neighborhood.py index 5017195..31cfec8 100644 --- a/porchlight/neighborhood.py +++ b/porchlight/neighborhood.py @@ -461,14 +461,14 @@ def order_doors(self, order: List[str]): a key in `Neighborhood._doors`. """ # The order list must: - # + Contain all doors at least once. + # + Contain all doors once. # + All doors must already exist and be known. if not order: msg = f"Empty or invalid input: {order}." logger.error(msg) raise ValueError(msg) - elif len(set(order)) > len(self._doors): + elif len(order) > len(self._doors): msg = ( f"More labels provided than doors " f"({len(order)} labels vs {len(self._doors)} doors)." From 1ed45f4109d9ec7159fc8eefcbd21413069ade40 Mon Sep 17 00:00:00 2001 From: "Teal, D" Date: Thu, 22 Dec 2022 15:56:02 -0800 Subject: [PATCH 20/24] Updates param repr to use data reprs. --- porchlight/param.py | 2 +- porchlight/tests/test_param.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/porchlight/param.py b/porchlight/param.py index 4cdc397..10195d2 100644 --- a/porchlight/param.py +++ b/porchlight/param.py @@ -105,7 +105,7 @@ def __repr__(self): "type": self.type, } - infostrings = [f"{key}={value}" for key, value in info.items()] + infostrings = [f"{key}={repr(value)}" for key, value in info.items()] outstr = f"Param({', '.join(infostrings)})" diff --git a/porchlight/tests/test_param.py b/porchlight/tests/test_param.py index 3f9a7f7..f5e4fc3 100644 --- a/porchlight/tests/test_param.py +++ b/porchlight/tests/test_param.py @@ -55,7 +55,7 @@ def test___eq__(self): def test___repr__(self): param1 = param.Param("x", 1) expected_string = ( - "Param(name=x, value=1, constant=False, " "type=)" + "Param(name='x', value=1, constant=False, " "type=)" ) self.assertEqual(repr(param1), expected_string) From a87f7363f06f4f8ec07e29fa6bb4c6a265eeb976 Mon Sep 17 00:00:00 2001 From: "Teal, D" Date: Fri, 23 Dec 2022 12:43:19 -0800 Subject: [PATCH 21/24] Removing nuance that is no longer relevant. --- docs/other/quickstart.rst | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/docs/other/quickstart.rst b/docs/other/quickstart.rst index 7af4826..bb4a503 100644 --- a/docs/other/quickstart.rst +++ b/docs/other/quickstart.rst @@ -298,21 +298,6 @@ Closing Nuances - |Neighborhood| objects will execute their functions sequentially, in the order they are added. if you'd like to re-order the functions before execution, see :py:meth:`~porchlight.neighborhood.Neighborhood.order_doors`. -- As of v0.4.0, there are a number of `known bugs - `_. - In particular, there is an issue with some special functions being imported. - You can readily circumvent this by writing a basic wrapper: - -.. code-block:: python - - from numpy import cos # ufuncs aren't supported - from porchlight import Door - - @Door - def my_cos(x): - '''Replace x and y with whatever variables you need.''' - y = cos(x) - return y - |porchlight| is under active development. The current development strategy will not include a dedicated stable branch until v1.0.0. That means that you From 2515ac715a65402dff4fb2ad71bbd3e80c27e82b Mon Sep 17 00:00:00 2001 From: "Teal, D" Date: Fri, 23 Dec 2022 12:44:39 -0800 Subject: [PATCH 22/24] Bumping porchlight version. --- docs/conf.py | 2 +- pyproject.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/conf.py b/docs/conf.py index dd2833f..b2616fd 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -23,7 +23,7 @@ author = "D J Teal" # The full version, including alpha/beta/rc tags -release = "0.4.0" +release = "0.5.0" # -- General configuration --------------------------------------------------- diff --git a/pyproject.toml b/pyproject.toml index 9bdd9c3..a38fbc5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "porchlight" -version = "0.4.0" +version = "0.5.0" description = "A function-managing package for models and systems with shared variables." authors = ["Teal, D "] license = "GNU General Public License v3.0 or later" From 323664e1518c6587c165a23fea78bb0db468facf Mon Sep 17 00:00:00 2001 From: "Teal, D" Date: Fri, 23 Dec 2022 12:48:25 -0800 Subject: [PATCH 23/24] Removing unused imports --- porchlight/door.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/porchlight/door.py b/porchlight/door.py index 3bf521f..8daf850 100644 --- a/porchlight/door.py +++ b/porchlight/door.py @@ -1,9 +1,7 @@ """ .. |Basedoor| replace:: :py:class:`~porchlight.door.BaseDoor` """ -import functools import inspect -import itertools import re import types From ff34b19bad23786ce064ef77787f3403df54f866 Mon Sep 17 00:00:00 2001 From: "Teal, D" Date: Fri, 23 Dec 2022 12:55:29 -0800 Subject: [PATCH 24/24] pre-commit updates --- .pre-commit-config.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 89368b2..b1ce73b 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,6 +1,6 @@ repos: - repo: https://github.com/psf/black - rev: 21.12b0 + rev: 22.12.0 hooks: - id: black args: @@ -30,5 +30,5 @@ repos: - id: flake8 args: - "--max-line-length=79" - - "--max-complexity=18" + - "--max-complexity=10" - "--ignore=F401,W503"