Skip to content

Commit

Permalink
Ensuring private_data really is private by limiting the possible keys…
Browse files Browse the repository at this point in the history
… to substrings of the caller's scope
  • Loading branch information
emma58 committed Feb 15, 2024
1 parent cc54ae5 commit 73977aa
Show file tree
Hide file tree
Showing 2 changed files with 48 additions and 12 deletions.
14 changes: 12 additions & 2 deletions 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 @@ -2029,7 +2029,17 @@ def _create_objects_for_deepcopy(self, memo, component_list):
comp._create_objects_for_deepcopy(memo, component_list)
return _ans

def private_data(self, scope):
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:
Expand Down
46 changes: 36 additions & 10 deletions pyomo/core/tests/unit/test_block.py
Original file line number Diff line number Diff line change
Expand Up @@ -3412,18 +3412,44 @@ def test_private_data(self):
m.b = Block()
m.b.b = Block([1, 2])

mfe = m.private_data('my_scope')
mfe = m.private_data()
self.assertIsInstance(mfe, dict)
mfe2 = m.private_data('another_scope')
self.assertIsInstance(mfe2, dict)
self.assertEqual(len(m._private_data), 2)
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'])

mfe = m.b.private_data('my_scope')
self.assertIsInstance(mfe, dict)
mfe1 = m.b.b[1].private_data('no mice here')
self.assertIsInstance(mfe1, dict)
mfe2 = m.b.b[2].private_data('no mice here')
self.assertIsInstance(mfe2, dict)
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)

# mfe2 = m.private_data('another_scope')
# self.assertIsInstance(mfe2, dict)
# self.assertEqual(len(m._private_data), 2)

# mfe = m.b.private_data('my_scope')
# self.assertIsInstance(mfe, dict)
# mfe1 = m.b.b[1].private_data('no mice here')
# self.assertIsInstance(mfe1, dict)
# mfe2 = m.b.b[2].private_data('no mice here')
# self.assertIsInstance(mfe2, dict)


if __name__ == "__main__":
Expand Down

0 comments on commit 73977aa

Please sign in to comment.