From f528eee67db0c2558740d97b04f80290200bd7d9 Mon Sep 17 00:00:00 2001 From: John Stilley <1831479+john-science@users.noreply.github.com> Date: Fri, 20 Dec 2024 09:08:22 -0800 Subject: [PATCH 1/2] Removing dependence on six (#2037) --- armi/cases/case.py | 4 ++-- armi/cli/entryPoint.py | 5 +---- armi/mpiActions.py | 7 +++---- armi/nuclearDataIO/cccc/tests/test_cccc.py | 4 +--- armi/nuclearDataIO/tests/test_xsLibraries.py | 8 ++++---- .../neutronics/tests/test_crossSectionManager.py | 8 ++++---- armi/reactor/blockParameters.py | 4 +--- armi/reactor/composites.py | 6 ++---- armi/reactor/parameters/parameterCollections.py | 15 +++++++-------- armi/reactor/tests/test_reactors.py | 8 ++++---- armi/tests/mockRunLogs.py | 14 +++++++------- armi/utils/iterables.py | 8 +++----- 12 files changed, 39 insertions(+), 52 deletions(-) diff --git a/armi/cases/case.py b/armi/cases/case.py index faebff6dd..588b020c4 100644 --- a/armi/cases/case.py +++ b/armi/cases/case.py @@ -28,6 +28,7 @@ import ast import cProfile import glob +import io import os import pathlib import pstats @@ -38,7 +39,6 @@ import trace import coverage -import six from armi import context from armi import getPluginManager @@ -492,7 +492,7 @@ def _endProfiling(profiler=None): profiler.disable() profiler.dump_stats("profiler.{:0>3}.stats".format(context.MPI_RANK)) - statsStream = six.StringIO() + statsStream = io.StringIO() summary = pstats.Stats(profiler, stream=statsStream).sort_stats("cumulative") summary.print_stats() if context.MPI_SIZE > 0 and context.MPI_COMM is not None: diff --git a/armi/cli/entryPoint.py b/armi/cli/entryPoint.py index bef74f07d..d6832c708 100644 --- a/armi/cli/entryPoint.py +++ b/armi/cli/entryPoint.py @@ -20,8 +20,6 @@ import argparse from typing import Optional, Union -import six - from armi import context, runLog, settings @@ -46,8 +44,7 @@ def __new__(mcs, name, bases, attrs): return type.__new__(mcs, name, bases, attrs) -@six.add_metaclass(_EntryPointEnforcer) -class EntryPoint: +class EntryPoint(metaclass=_EntryPointEnforcer): """ Generic command line entry point. diff --git a/armi/mpiActions.py b/armi/mpiActions.py index 8026ddf1d..ca3fc9ca3 100644 --- a/armi/mpiActions.py +++ b/armi/mpiActions.py @@ -56,10 +56,9 @@ import collections import gc import math +import pickle import timeit -from six.moves import cPickle - from armi import context from armi import interfaces from armi import runLog @@ -138,7 +137,7 @@ def _mpiOperationHelper(self, obj, mpiFunction): self.o = self.r = self.cs = None try: return mpiFunction(obj, root=0) - except cPickle.PicklingError as error: + except pickle.PicklingError as error: runLog.error("Failed to {} {}.".format(mpiFunction.__name__, obj)) runLog.error(error) raise @@ -539,7 +538,7 @@ def invokeHook(self): # or how the interfaces are distributed. self.r._markSynchronized() - except (cPickle.PicklingError, TypeError) as error: + except (pickle.PicklingError, TypeError) as error: runLog.error("Failed to transmit on distribute state root MPI bcast") runLog.error(error) # workers are still waiting for a reactor object diff --git a/armi/nuclearDataIO/cccc/tests/test_cccc.py b/armi/nuclearDataIO/cccc/tests/test_cccc.py index 6f9ea18ee..33b16c2b8 100644 --- a/armi/nuclearDataIO/cccc/tests/test_cccc.py +++ b/armi/nuclearDataIO/cccc/tests/test_cccc.py @@ -15,8 +15,6 @@ import io import unittest -import six - from armi.nuclearDataIO import cccc @@ -104,4 +102,4 @@ def setUpClass(cls): cls.readerClass = cccc.AsciiRecordReader def setUp(self): - self.streamCls = six.StringIO + self.streamCls = io.StringIO diff --git a/armi/nuclearDataIO/tests/test_xsLibraries.py b/armi/nuclearDataIO/tests/test_xsLibraries.py index ae4b55229..88fd59ad3 100644 --- a/armi/nuclearDataIO/tests/test_xsLibraries.py +++ b/armi/nuclearDataIO/tests/test_xsLibraries.py @@ -15,11 +15,11 @@ import copy import filecmp import os +import pickle import traceback import unittest import numpy as np -from six.moves import cPickle from armi.nucDirectory import nuclideBases from armi.nuclearDataIO import xsLibraries @@ -87,15 +87,15 @@ def setUpClass(cls): cls.xsLibGenerationErrorStack = traceback.format_exc() def test_canPickleAndUnpickleISOTXS(self): - pikAA = cPickle.loads(cPickle.dumps(self.isotxsAA)) + pikAA = pickle.loads(pickle.dumps(self.isotxsAA)) self.assertTrue(xsLibraries.compare(pikAA, self.isotxsAA)) def test_canPickleAndUnpickleGAMISO(self): - pikAA = cPickle.loads(cPickle.dumps(self.gamisoAA)) + pikAA = pickle.loads(pickle.dumps(self.gamisoAA)) self.assertTrue(xsLibraries.compare(pikAA, self.gamisoAA)) def test_canPickleAndUnpicklePMATRX(self): - pikAA = cPickle.loads(cPickle.dumps(self.pmatrxAA)) + pikAA = pickle.loads(pickle.dumps(self.pmatrxAA)) self.assertTrue(xsLibraries.compare(pikAA, self.pmatrxAA)) def test_compareWorks(self): diff --git a/armi/physics/neutronics/tests/test_crossSectionManager.py b/armi/physics/neutronics/tests/test_crossSectionManager.py index ec86ab3ea..219d852fb 100644 --- a/armi/physics/neutronics/tests/test_crossSectionManager.py +++ b/armi/physics/neutronics/tests/test_crossSectionManager.py @@ -19,12 +19,11 @@ """ import copy import os +import pickle import unittest from io import BytesIO from unittest.mock import MagicMock -from six.moves import cPickle - from armi import settings from armi.physics.neutronics import crossSectionGroupManager from armi.physics.neutronics.const import CONF_CROSS_SECTION @@ -73,9 +72,9 @@ def test_getBlocksInGroup(self): def test_is_pickleable(self): self.bc.weightingParam = "test" buf = BytesIO() - cPickle.dump(self.bc, buf) + pickle.dump(self.bc, buf) buf.seek(0) - newBc = cPickle.load(buf) + newBc = pickle.load(buf) self.assertEqual(self.bc.weightingParam, newBc.weightingParam) @@ -85,6 +84,7 @@ def setUp(self): for bi, b in enumerate(self.blockList): b.setType("fuel") b.p.percentBu = bi / 4.0 * 100 + self.blockList[0], self.blockList[2] = self.blockList[2], self.blockList[0] self.bc = MedianBlockCollection( self.blockList[0].core.r.blueprints.allNuclidesInProblem diff --git a/armi/reactor/blockParameters.py b/armi/reactor/blockParameters.py index 8952bfc97..e96010cbd 100644 --- a/armi/reactor/blockParameters.py +++ b/armi/reactor/blockParameters.py @@ -13,8 +13,6 @@ # limitations under the License. """Parameter definitions for Blocks.""" -import six - from armi import runLog from armi.physics.neutronics import crossSectionGroupManager from armi.reactor import parameters @@ -207,7 +205,7 @@ def envGroup(self, envGroupChar): ) self.envGroupNum = intValue return - elif not isinstance(envGroupChar, six.string_types): + elif not isinstance(envGroupChar, str): raise Exception( f"Wrong type for envGroupChar {envGroupChar}: {type(envGroupChar)}" ) diff --git a/armi/reactor/composites.py b/armi/reactor/composites.py index 62a0e329d..1b2bad687 100644 --- a/armi/reactor/composites.py +++ b/armi/reactor/composites.py @@ -39,7 +39,6 @@ from typing import Dict, List, Optional, Tuple, Type, Union import numpy as np -import six from armi import context, runLog, utils from armi.nucDirectory import elements, nucDir, nuclideBases @@ -759,10 +758,9 @@ def hasFlags(self, typeID: TypeSpec, exact=False): """ if not typeID: return not exact - if isinstance(typeID, six.string_types): + if isinstance(typeID, str): raise TypeError( - "Must pass Flags, or an iterable of Flags; Strings are no longer " - "supported" + "Must pass Flags, or an iterable of Flags; Strings are no longer supported" ) elif not isinstance(typeID, Flags): diff --git a/armi/reactor/parameters/parameterCollections.py b/armi/reactor/parameters/parameterCollections.py index e3cc0d886..c588563cc 100644 --- a/armi/reactor/parameters/parameterCollections.py +++ b/armi/reactor/parameters/parameterCollections.py @@ -12,13 +12,12 @@ # See the License for the specific language governing permissions and # limitations under the License. +from typing import Any, Optional, List, Set, Iterator, Callable import copy import pickle -from typing import Any, Optional, List, Set, Iterator, Callable import sys import numpy as np -import six from armi import runLog from armi.reactor.parameters import parameterDefinitions, exceptions @@ -38,10 +37,10 @@ This is a counter of the number of instances of all types. They are useful for tracking items through the history of a database. -.. warning:: - - This is not MPI safe. We also have not done anything to make it thread safe, - except that the GIL exists. +Warning +------- +This is not MPI safe. We also have not done anything to make it thread safe, except that the GIL +exists. """ @@ -365,7 +364,7 @@ def __setitem__(self, name, value): ) def __delitem__(self, name): - if isinstance(name, six.string_types): + if isinstance(name, str): pd = self.paramDefs[name] if hasattr(self, pd.fieldName): pd.assigned = SINCE_ANYTHING @@ -374,7 +373,7 @@ def __delitem__(self, name): del self._hist[name] def __contains__(self, name): - if isinstance(name, six.string_types): + if isinstance(name, str): return hasattr(self, "_p_" + name) else: return name in self._hist diff --git a/armi/reactor/tests/test_reactors.py b/armi/reactor/tests/test_reactors.py index 6187155e6..ea03fbaa5 100644 --- a/armi/reactor/tests/test_reactors.py +++ b/armi/reactor/tests/test_reactors.py @@ -15,12 +15,12 @@ import copy import logging import os +import pickle import unittest from math import sqrt from unittest.mock import patch from numpy.testing import assert_allclose, assert_equal -from six.moves import cPickle from armi import operators from armi import runLog @@ -170,7 +170,7 @@ def loadTestReactor( if isPickeledReactor and TEST_REACTOR: # return test reactor only if no custom settings are needed. - o, r, assemNum = cPickle.loads(TEST_REACTOR) + o, r, assemNum = pickle.loads(TEST_REACTOR) o.reattach(r, o.cs) return o, r @@ -196,7 +196,7 @@ def loadTestReactor( if isPickeledReactor: # cache it for fast load for other future tests # protocol=2 allows for classes with __slots__ but not __getstate__ to be pickled - TEST_REACTOR = cPickle.dumps((o, o.r, o.r.p.maxAssemNum), protocol=2) + TEST_REACTOR = pickle.dumps((o, o.r, o.r.p.maxAssemNum), protocol=2) return o, o.r @@ -934,7 +934,7 @@ def test_getMass(self): assert_allclose(mass1, mass2) def test_isPickleable(self): - loaded = cPickle.loads(cPickle.dumps(self.r)) + loaded = pickle.loads(pickle.dumps(self.r)) # ensure we didn't break the current reactor self.assertIs(self.r.core.spatialGrid.armiObject, self.r.core) diff --git a/armi/tests/mockRunLogs.py b/armi/tests/mockRunLogs.py index bc10db3a5..38b5c7462 100644 --- a/armi/tests/mockRunLogs.py +++ b/armi/tests/mockRunLogs.py @@ -12,20 +12,20 @@ # See the License for the specific language governing permissions and # limitations under the License. """ -This module contains subclasses of the armi.runLog._RunLog class that can be used to determine whether or not -one of the specific methods were called. These should only be used in testing. +This module contains subclasses of the armi.runLog._RunLog class that can be used to determine +whether or not one of the specific methods were called. These should only be used in testing. """ -import six +import io import sys from armi import runLog class BufferLog(runLog._RunLog): - r"""Log which captures the output in attributes instead of emitting them. + """Log which captures the output in attributes instead of emitting them. - Used mostly in testing to ensure certain things get output, or to prevent any output - from showing. + Used mostly in testing to ensure certain things get output, or to prevent any output from + showing. """ def __init__(self, *args, **kwargs): @@ -34,7 +34,7 @@ def __init__(self, *args, **kwargs): self._outputStream = "" self._singleMessageCounts = {} self._singleWarningMessageCounts = {} - self._errStream = six.StringIO() + self._errStream = io.StringIO() sys.stderr = self._errStream self.setVerbosity(0) diff --git a/armi/utils/iterables.py b/armi/utils/iterables.py index ce204aa0a..70fafc0a2 100644 --- a/armi/utils/iterables.py +++ b/armi/utils/iterables.py @@ -13,11 +13,9 @@ # limitations under the License. """Module of utilities to help dealing with iterable objects in Python.""" -from itertools import tee, chain +from itertools import chain, filterfalse, tee import struct -from six.moves import filterfalse, map, xrange, filter - import numpy as np @@ -43,7 +41,7 @@ def chunk(lst, n): >>> list(chunk([1,2,3,4,5,6,7,8,9,10], 4)) [[1, 2, 3, 4], [5, 6, 7, 8], [9, 10]] """ - for i in xrange(0, len(lst), n): + for i in range(0, len(lst), n): yield lst[i : i + n] @@ -84,7 +82,7 @@ def split(a, n, padWith=()): k, m = divmod(N, n) chunked = [ - a[i * k + min(i, m) : (i + 1) * k + min(i + 1, m)] or padWith for i in xrange(n) + a[i * k + min(i, m) : (i + 1) * k + min(i + 1, m)] or padWith for i in range(n) ] return chunked From f86ef61964cab1be8064468627906d85519788eb Mon Sep 17 00:00:00 2001 From: John Stilley <1831479+john-science@users.noreply.github.com> Date: Fri, 20 Dec 2024 09:29:12 -0800 Subject: [PATCH 2/2] Identifying all first-order dependencies (#2039) --- pyproject.toml | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index a1b4c6a2f..330094aa1 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -31,8 +31,8 @@ authors = [ ] dependencies = [ "coverage>=7.2.0", # Code coverage tool. Sadly baked into every Case. - "h5py>=3.9 ; python_version >= '3.11.0'", # Needed because our database files are H5 format "h5py>=3.0,<=3.9 ; python_version < '3.11.0'", + "h5py>=3.9 ; python_version >= '3.11.0'", # Needed because our database files are H5 format "htmltree>=0.7.6", # Our reports have HTML output "matplotlib>=3.5.3,<3.8.0", # Important plotting library "numpy>=1.21", # Important math library @@ -40,8 +40,9 @@ dependencies = [ "pluggy>=1.2.0", # Central tool behind the ARMI Plugin system "pyDOE>=0.3.8", # We import a Latin-hypercube algorithm to explore a phase space "pyevtk>=1.2.0", # Handles binary VTK visualization files - "ruamel.yaml.clib ; python_version >= '3.11.0'", # C-based core of ruamel below + "python-dateutil>=2.2", # To read a datetime string more easily. "ruamel.yaml ; python_version >= '3.11.0'", # Our foundational YAML library + "ruamel.yaml.clib ; python_version >= '3.11.0'", # C-based core of ruamel below "ruamel.yaml.clib<=0.2.7 ; python_version < '3.11.0'", # C-based core of ruamel below "ruamel.yaml<=0.17.21 ; python_version < '3.11.0'", # Our foundational YAML library "scipy>=1.7.0", # Used for curve-fitting and matrix math @@ -81,9 +82,10 @@ test = [ "ipykernel>=6.0.0", # IPython Kernel (We run test notebooks from the doc tutorials.) "jupyter_client>=7.0.0", # Reference implementation of the Jupyter protocol "nbconvert>=7.0.0", # Converting Jupyter Notebooks to other formats - "pytest>=7.0.0", # Our primary test tooling + "nbformat>=5.5.0", # Jupyter Notebook reader "pytest-cov>=4.0.0", # coverage plugin "pytest-xdist>=3.0.0", # To spread our tests over multiple CPUs + "pytest>=7.0.0", # Our primary test tooling "ruff==0.5.1", # Linting and code formatting (version-pinned) ] docs = [