Skip to content

Commit

Permalink
Merge pull request #3138 from emma58/add-mfe
Browse files Browse the repository at this point in the history
Adding private_data to `_BlockData`
  • Loading branch information
jsiirola authored Feb 16, 2024
2 parents 609e755 + 170acb8 commit 0ed5c59
Show file tree
Hide file tree
Showing 2 changed files with 52 additions and 1 deletion.
20 changes: 19 additions & 1 deletion pyomo/core/base/block.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
import textwrap
from contextlib import contextmanager

from inspect import isclass
from inspect import isclass, currentframe
from itertools import filterfalse, chain
from operator import itemgetter, attrgetter
from io import StringIO
Expand Down Expand Up @@ -550,6 +550,7 @@ def __init__(self, component):
super(_BlockData, self).__setattr__('_ctypes', {})
super(_BlockData, self).__setattr__('_decl', {})
super(_BlockData, self).__setattr__('_decl_order', [])
self._private_data = None

def __getattr__(self, val):
if val in ModelComponentFactory:
Expand Down Expand Up @@ -1985,6 +1986,23 @@ def _create_objects_for_deepcopy(self, memo, component_list):
comp._create_objects_for_deepcopy(memo, component_list)
return _ans

def private_data(self, scope=None):
mod = currentframe().f_back.f_globals['__name__']
if scope is None:
scope = mod
elif not mod.startswith(scope):
raise ValueError(
"All keys in the 'private_data' dictionary must "
"be substrings of the caller's module name. "
"Received '%s' when calling private_data on Block "
"'%s'." % (scope, self.name)
)
if self._private_data is None:
self._private_data = {}
if scope not in self._private_data:
self._private_data[scope] = {}
return self._private_data[scope]


@ModelComponentFactory.register(
"A component that contains one or more model components."
Expand Down
33 changes: 33 additions & 0 deletions pyomo/core/tests/unit/test_block.py
Original file line number Diff line number Diff line change
Expand Up @@ -3404,6 +3404,39 @@ def test_deduplicate_component_data_iterindex(self):
],
)

def test_private_data(self):
m = ConcreteModel()
m.b = Block()
m.b.b = Block([1, 2])

mfe = m.private_data()
self.assertIsInstance(mfe, dict)
self.assertEqual(len(mfe), 0)
self.assertEqual(len(m._private_data), 1)
self.assertIn('pyomo.core.tests.unit.test_block', m._private_data)
self.assertIs(mfe, m._private_data['pyomo.core.tests.unit.test_block'])

with self.assertRaisesRegex(
ValueError,
"All keys in the 'private_data' dictionary must "
"be substrings of the caller's module name. "
"Received 'no mice here' when calling private_data on Block "
"'b'.",
):
mfe2 = m.b.private_data('no mice here')

mfe3 = m.b.b[1].private_data('pyomo.core.tests')
self.assertIsInstance(mfe3, dict)
self.assertEqual(len(mfe3), 0)
self.assertIsInstance(m.b.b[1]._private_data, dict)
self.assertEqual(len(m.b.b[1]._private_data), 1)
self.assertIn('pyomo.core.tests', m.b.b[1]._private_data)
self.assertIs(mfe3, m.b.b[1]._private_data['pyomo.core.tests'])
mfe3['there are cookies'] = 'but no mice'

mfe4 = m.b.b[1].private_data('pyomo.core.tests')
self.assertIs(mfe4, mfe3)


if __name__ == "__main__":
unittest.main()

0 comments on commit 0ed5c59

Please sign in to comment.