KingsburyLab / pyEQL

A Python library for solution chemistry
Other
65 stars 20 forks source link

CI: parallel pytest execution causes intermittent test failures #170

Open rkingsbury opened 2 months ago

rkingsbury commented 2 months ago

I recently modified the GitHub actions testing workflow to use pytest-xdist to run the unit tests in parallel. This resulted in a nice speedup, but also occasionally causes a test failure with the exception EOFError: Ran out of input. This is apparently related to multiple parallel workers trying to read or write (not sure which) to the same file at the same time.

It's unclear to me which test(s) trigger this. My non-scientific observation is that the error is always triggered by test_activity.py (although I could be wrong about that) and it seems to have something to do with accessing the pint cache. See full stacktrace from an example failed CI test run below.

Manually re-running the tests usually result in success, so this is a non-deterministic failure. We need to either find the offending unit test and modify it, or (less preferred) go back to running unit tests in serial.

Run pytest -n auto --cov=src/pyEQL --cov-report=xml
============================= test session starts ==============================
platform linux -- Python 3.10.14, pytest-8.3.2, pluggy-1.5.0 -- /opt/hostedtoolcache/Python/3.10.14/x64/bin/python
cachedir: .pytest_cache
rootdir: /home/runner/work/pyEQL/pyEQL
configfile: pyproject.toml
testpaths: tests
plugins: cov-5.0.0, xdist-3.6.1
created: 4/4 workers
4 workers [134 items]

scheduling tests via LoadScheduling

==================================== ERRORS ====================================
___________________ ERROR collecting tests/test_activity.py ____________________
tests/test_activity.py:18: in <module>
    from pyEQL.activity_correction import _debye_parameter_activity, _debye_parameter_B
        __builtins__ = <builtins>
        __cached__ = '/home/runner/work/pyEQL/pyEQL/tests/__pycache__/test_activity.cpython-310.pyc'
        __doc__    = '\npyEQL activity correction methods test suite\n============================================\n\nThis file contains te...ve functions. In some\ncases, the output is also tested against a well-established model published\nby USGS(PHREEQC)\n'
        __file__   = '/home/runner/work/pyEQL/pyEQL/tests/test_activity.py'
        __loader__ = <_pytest.assertion.rewrite.AssertionRewritingHook object at 0x7ff017d74af0>
        __name__   = 'tests.test_activity'
        __package__ = 'tests'
        __spec__   = ModuleSpec(name='tests.test_activity', loader=<_pytest.assertion.rewrite.AssertionRewritingHook object at 0x7ff017d74af0>, origin='/home/runner/work/pyEQL/pyEQL/tests/test_activity.py')
        np         = <module 'numpy' from '/opt/hostedtoolcache/Python/3.10.14/x64/lib/python3.10/site-packages/numpy/__init__.py'>
        platform   = <module 'platform' from '/opt/hostedtoolcache/Python/3.10.14/x64/lib/python3.10/platform.py'>
        pytest     = <module 'pytest' from '/opt/hostedtoolcache/Python/3.10.14/x64/lib/python3.10/site-packages/pytest/__init__.py'>
src/pyEQL/__init__.py:36: in <module>
    ureg = UnitRegistry(cache_folder=":auto:")
        JSONStore  = <class 'maggma.stores.mongolike.JSONStore'>
        UnitRegistry = <class 'pint.registry.UnitRegistry'>
        __builtins__ = <builtins>
        __cached__ = '/home/runner/work/pyEQL/pyEQL/src/pyEQL/__pycache__/__init__.cpython-310.pyc'
        __doc__    = '\npyEQL is a python package for calculating the properties of aqueous solutions\nand performing chemical thermodynamics computations.\n\n:copyright: 2013-2024 by Ryan S. Kingsbury\n:license: LGPL, see LICENSE for more details.\n'
        __file__   = '/home/runner/work/pyEQL/pyEQL/src/pyEQL/__init__.py'
        __loader__ = <_frozen_importlib_external.SourceFileLoader object at 0x7ff00b4de980>
        __name__   = 'pyEQL'
        __package__ = 'pyEQL'
        __path__   = ['/home/runner/work/pyEQL/pyEQL/src/pyEQL']
        __spec__   = ModuleSpec(name='pyEQL', loader=<_frozen_importlib_external.SourceFileLoader object at 0x7ff00b4de980>, origin='/home/runner/work/pyEQL/pyEQL/src/pyEQL/__init__.py', submodule_search_locations=['/home/runner/work/pyEQL/pyEQL/src/pyEQL'])
        __version__ = '0.0.post1.dev1+g893cdb8'
        dist_name  = 'pyEQL'
        files      = <function files at 0x7ff00b170b80>
        logger     = <Logger pyEQL (WARNING)>
        logging    = <module 'logging' from '/opt/hostedtoolcache/Python/3.10.14/x64/lib/python3.10/logging/__init__.py'>
/opt/hostedtoolcache/Python/3.10.14/x64/lib/python3.10/site-packages/pint/facets/plain/registry.py:153: in __call__
    obj._after_init()
        __class__  = <class 'pint.facets.plain.registry.RegistryMeta'>
        args       = ()
        kwargs     = {'cache_folder': ':auto:'}
        obj        = <pint.registry.UnitRegistry object at 0x7ff007dce440>
        self       = <class 'pint.registry.UnitRegistry'>
/opt/hostedtoolcache/Python/3.10.14/x64/lib/python3.10/site-packages/pint/facets/system/registry.py:76: in _after_init
    super()._after_init()
        __class__  = <class 'pint.facets.system.registry.GenericSystemRegistry'>
        self       = <pint.registry.UnitRegistry object at 0x7ff007dce440>
/opt/hostedtoolcache/Python/3.10.14/x64/lib/python3.10/site-packages/pint/facets/group/registry.py:64: in _after_init
    super()._after_init()
        __class__  = <class 'pint.facets.group.registry.GenericGroupRegistry'>
        self       = <pint.registry.UnitRegistry object at 0x7ff007dce440>
/opt/hostedtoolcache/Python/3.10.14/x64/lib/python3.10/site-packages/pint/facets/plain/registry.py:329: in _after_init
    loaded_files = self.load_definitions(path, True)
        path       = PosixPath('/opt/hostedtoolcache/Python/3.10.14/x64/lib/python3.10/site-packages/pint/default_en.txt')
        self       = <pint.registry.UnitRegistry object at 0x7ff007dce440>
/opt/hostedtoolcache/Python/3.10.14/x64/lib/python3.10/site-packages/pint/facets/plain/registry.py:594: in load_definitions
    parsed_project = self._def_parser.parse_file(file)
        file       = PosixPath('/opt/hostedtoolcache/Python/3.10.14/x64/lib/python3.10/site-packages/pint/default_en.txt')
        is_resource = True
        self       = <pint.registry.UnitRegistry object at 0x7ff007dce440>
/opt/hostedtoolcache/Python/3.10.14/x64/lib/python3.10/site-packages/pint/delegates/txt_defparser/defparser.py:124: in parse_file
    return fp.parse(
        cfg        = None
        filename   = PosixPath('/opt/hostedtoolcache/Python/3.10.14/x64/lib/python3.10/site-packages/pint/default_en.txt')
        self       = <pint.delegates.txt_defparser.defparser.DefParser object at 0x7ff007dce500>
/opt/hostedtoolcache/Python/3.10.14/x64/lib/python3.10/site-packages/flexparser/flexparser.py:1644: in parse
    pp[(source_location, target)] = parsed = parser.parse(
        CustomParser = <class 'pint.delegates.txt_defparser.defparser._PintParser'>
        config     = ParserConfig(non_int_type=<class 'float'>)
        delimiters = {'\n': (<DelimiterInclude.SPLIT: 1>, <DelimiterAction.CONTINUE: 1>), '\r': (<DelimiterInclude.SPLIT: 1>, <DelimiterAct..., <DelimiterAction.CONTINUE: 1>), '#': (<DelimiterInclude.SPLIT_BEFORE: 3>, <DelimiterAction.CAPTURE_NEXT_TIL_EOL: 2>)}
        entry_point = PosixPath('/opt/hostedtoolcache/Python/3.10.14/x64/lib/python3.10/site-packages/pint/default_en.txt')
        extra_parser_kwargs = {'diskcache': <pint.delegates.base_defparser.build_disk_cache_class.<locals>.PintDiskCache object at 0x7ff007dce410>}
        locator    = <function default_locator at 0x7ff0080e9000>
        parsed     = ParsedSource(parsed_source=PintRootBlock(opening=BOF(start_line=0, start_col=0, end_line=0, end_col=0, raw=None, conte...99, start_col=0, end_line=899, end_col=0, raw=None), delimiters={}), config=ParserConfig(non_int_type=<class 'float'>))
        parser     = <pint.delegates.txt_defparser.defparser._PintParser object at 0x7ff007dfe410>
        pending    = []
        pp         = {None: ParsedSource(parsed_source=PintRootBlock(opening=BOF(start_line=0, start_col=0, end_line=0, end_col=0, raw=None...9, start_col=0, end_line=899, end_col=0, raw=None), delimiters={}), config=ParserConfig(non_int_type=<class 'float'>))}
        prefer_resource_as_file = True
        source_location = PosixPath('/opt/hostedtoolcache/Python/3.10.14/x64/lib/python3.10/site-packages/pint/default_en.txt')
        spec       = <class 'pint.delegates.txt_defparser.defparser._PintParser'>
        strip_spaces = True
        target     = 'constants_en.txt'
/opt/hostedtoolcache/Python/3.10.14/x64/lib/python3.10/site-packages/flexparser/flexparser.py:1182: in parse
    return self.parse_file(source_location)
        self       = <pint.delegates.txt_defparser.defparser._PintParser object at 0x7ff007dfe410>
        source_location = PosixPath('/opt/hostedtoolcache/Python/3.10.14/x64/lib/python3.10/site-packages/pint/constants_en.txt')
/opt/hostedtoolcache/Python/3.10.14/x64/lib/python3.10/site-packages/pint/delegates/txt_defparser/defparser.py:58: in parse_file
    content, _basename = self._diskcache.load(path, super().parse_file)
        __class__  = <class 'pint.delegates.txt_defparser.defparser._PintParser'>
        path       = PosixPath('/opt/hostedtoolcache/Python/3.10.14/x64/lib/python3.10/site-packages/pint/constants_en.txt')
        self       = <pint.delegates.txt_defparser.defparser._PintParser object at 0x7ff007dfe410>
/opt/hostedtoolcache/Python/3.10.14/x64/lib/python3.10/site-packages/flexcache/flexcache.py:379: in load
    converted_object = self.rawload(header, cache_path)
        cache_path = PosixPath('/home/runner/.cache/pint/f5162aaf3660e5d66c2f8d5e987428b18449d72e.pickle')
        converter  = <bound method Parser.parse_file of <pint.delegates.txt_defparser.defparser._PintParser object at 0x7ff007dfe410>>
        converter_id = 'parse_file'
        header     = build_disk_cache_class.<locals>.PathHeader(source=PosixPath('/opt/hostedtoolcache/Python/3.10.14/x64/lib/python3.10/si...system='Linux', python_implementation='CPython', python_version='3.10.14', pint_version='0.24.3', non_int_type='float')
        header_class = <class 'pint.delegates.base_defparser.build_disk_cache_class.<locals>.PathHeader'>
        pass_hash  = False
        self       = <pint.delegates.base_defparser.build_disk_cache_class.<locals>.PintDiskCache object at 0x7ff007dce410>
        source_object = PosixPath('/opt/hostedtoolcache/Python/3.10.14/x64/lib/python3.10/site-packages/pint/constants_en.txt')
/opt/hostedtoolcache/Python/3.10.14/x64/lib/python3.10/site-packages/flexcache/flexcache.py:421: in rawload
    return pickle.load(fi)
E   EOFError: Ran out of input
        cache_path = PosixPath('/home/runner/.cache/pint/f5162aaf3660e5d66c2f8d5e987428b18449d72e.pickle')
        fi         = <_io.BufferedReader name='/home/runner/.cache/pint/f5162aaf3660e5d66c2f8d5e987428b18449d72e.pickle'>
        header     = build_disk_cache_class.<locals>.PathHeader(source=PosixPath('/opt/hostedtoolcache/Python/3.10.14/x64/lib/python3.10/si...system='Linux', python_implementation='CPython', python_version='3.10.14', pint_version='0.24.3', non_int_type='float')
        self       = <pint.delegates.base_defparser.build_disk_cache_class.<locals>.PintDiskCache object at 0x7ff007dce410>
___________________ ERROR collecting tests/test_activity.py ____________________
tests/test_activity.py:18: in <module>
    from pyEQL.activity_correction import _debye_parameter_activity, _debye_parameter_B
        __builtins__ = <builtins>
        __cached__ = '/home/runner/work/pyEQL/pyEQL/tests/__pycache__/test_activity.cpython-310.pyc'
        __doc__    = '\npyEQL activity correction methods test suite\n============================================\n\nThis file contains te...ve functions. In some\ncases, the output is also tested against a well-established model published\nby USGS(PHREEQC)\n'
        __file__   = '/home/runner/work/pyEQL/pyEQL/tests/test_activity.py'
        __loader__ = <_pytest.assertion.rewrite.AssertionRewritingHook object at 0x7fb968790af0>
        __name__   = 'tests.test_activity'
        __package__ = 'tests'
        __spec__   = ModuleSpec(name='tests.test_activity', loader=<_pytest.assertion.rewrite.AssertionRewritingHook object at 0x7fb968790af0>, origin='/home/runner/work/pyEQL/pyEQL/tests/test_activity.py')
        np         = <module 'numpy' from '/opt/hostedtoolcache/Python/3.10.14/x64/lib/python3.10/site-packages/numpy/__init__.py'>
        platform   = <module 'platform' from '/opt/hostedtoolcache/Python/3.10.14/x64/lib/python3.10/platform.py'>
        pytest     = <module 'pytest' from '/opt/hostedtoolcache/Python/3.10.14/x64/lib/python3.10/site-packages/pytest/__init__.py'>
src/pyEQL/__init__.py:36: in <module>
    ureg = UnitRegistry(cache_folder=":auto:")
        JSONStore  = <class 'maggma.stores.mongolike.JSONStore'>
        UnitRegistry = <class 'pint.registry.UnitRegistry'>
        __builtins__ = <builtins>
        __cached__ = '/home/runner/work/pyEQL/pyEQL/src/pyEQL/__pycache__/__init__.cpython-310.pyc'
        __doc__    = '\npyEQL is a python package for calculating the properties of aqueous solutions\nand performing chemical thermodynamics computations.\n\n:copyright: 2013-2024 by Ryan S. Kingsbury\n:license: LGPL, see LICENSE for more details.\n'
        __file__   = '/home/runner/work/pyEQL/pyEQL/src/pyEQL/__init__.py'
        __loader__ = <_frozen_importlib_external.SourceFileLoader object at 0x7fb9615da9e0>
        __name__   = 'pyEQL'
        __package__ = 'pyEQL'
        __path__   = ['/home/runner/work/pyEQL/pyEQL/src/pyEQL']
        __spec__   = ModuleSpec(name='pyEQL', loader=<_frozen_importlib_external.SourceFileLoader object at 0x7fb9615da9e0>, origin='/home/runner/work/pyEQL/pyEQL/src/pyEQL/__init__.py', submodule_search_locations=['/home/runner/work/pyEQL/pyEQL/src/pyEQL'])
        __version__ = '0.0.post1.dev1+g893cdb8'
        dist_name  = 'pyEQL'
        files      = <function files at 0x7fb9615fcb80>
        logger     = <Logger pyEQL (WARNING)>
        logging    = <module 'logging' from '/opt/hostedtoolcache/Python/3.10.14/x64/lib/python3.10/logging/__init__.py'>
/opt/hostedtoolcache/Python/3.10.14/x64/lib/python3.10/site-packages/pint/facets/plain/registry.py:153: in __call__
    obj._after_init()
        __class__  = <class 'pint.facets.plain.registry.RegistryMeta'>
        args       = ()
        kwargs     = {'cache_folder': ':auto:'}
        obj        = <pint.registry.UnitRegistry object at 0x7fb9547c6470>
        self       = <class 'pint.registry.UnitRegistry'>
/opt/hostedtoolcache/Python/3.10.14/x64/lib/python3.10/site-packages/pint/facets/system/registry.py:76: in _after_init
    super()._after_init()
        __class__  = <class 'pint.facets.system.registry.GenericSystemRegistry'>
        self       = <pint.registry.UnitRegistry object at 0x7fb9547c6470>
/opt/hostedtoolcache/Python/3.10.14/x64/lib/python3.10/site-packages/pint/facets/group/registry.py:64: in _after_init
    super()._after_init()
        __class__  = <class 'pint.facets.group.registry.GenericGroupRegistry'>
        self       = <pint.registry.UnitRegistry object at 0x7fb9547c6470>
/opt/hostedtoolcache/Python/3.10.14/x64/lib/python3.10/site-packages/pint/facets/plain/registry.py:329: in _after_init
    loaded_files = self.load_definitions(path, True)
        path       = PosixPath('/opt/hostedtoolcache/Python/3.10.14/x64/lib/python3.10/site-packages/pint/default_en.txt')
        self       = <pint.registry.UnitRegistry object at 0x7fb9547c6470>
/opt/hostedtoolcache/Python/3.10.14/x64/lib/python3.10/site-packages/pint/facets/plain/registry.py:594: in load_definitions
    parsed_project = self._def_parser.parse_file(file)
        file       = PosixPath('/opt/hostedtoolcache/Python/3.10.14/x64/lib/python3.10/site-packages/pint/default_en.txt')
        is_resource = True
        self       = <pint.registry.UnitRegistry object at 0x7fb9547c6470>
/opt/hostedtoolcache/Python/3.10.14/x64/lib/python3.10/site-packages/pint/delegates/txt_defparser/defparser.py:124: in parse_file
    return fp.parse(
        cfg        = None
        filename   = PosixPath('/opt/hostedtoolcache/Python/3.10.14/x64/lib/python3.10/site-packages/pint/default_en.txt')
        self       = <pint.delegates.txt_defparser.defparser.DefParser object at 0x7fb9547c6530>
/opt/hostedtoolcache/Python/3.10.14/x64/lib/python3.10/site-packages/flexparser/flexparser.py:1644: in parse
    pp[(source_location, target)] = parsed = parser.parse(
        CustomParser = <class 'pint.delegates.txt_defparser.defparser._PintParser'>
        config     = ParserConfig(non_int_type=<class 'float'>)
        delimiters = {'\n': (<DelimiterInclude.SPLIT: 1>, <DelimiterAction.CONTINUE: 1>), '\r': (<DelimiterInclude.SPLIT: 1>, <DelimiterAct..., <DelimiterAction.CONTINUE: 1>), '#': (<DelimiterInclude.SPLIT_BEFORE: 3>, <DelimiterAction.CAPTURE_NEXT_TIL_EOL: 2>)}
        entry_point = PosixPath('/opt/hostedtoolcache/Python/3.10.14/x64/lib/python3.10/site-packages/pint/default_en.txt')
        extra_parser_kwargs = {'diskcache': <pint.delegates.base_defparser.build_disk_cache_class.<locals>.PintDiskCache object at 0x7fb9547c6440>}
        locator    = <function default_locator at 0x7fb954ae5000>
        parsed     = ParsedSource(parsed_source=PintRootBlock(opening=BOF(start_line=0, start_col=0, end_line=0, end_col=0, raw=None, conte...99, start_col=0, end_line=899, end_col=0, raw=None), delimiters={}), config=ParserConfig(non_int_type=<class 'float'>))
        parser     = <pint.delegates.txt_defparser.defparser._PintParser object at 0x7fb9547fa410>
        pending    = []
        pp         = {None: ParsedSource(parsed_source=PintRootBlock(opening=BOF(start_line=0, start_col=0, end_line=0, end_col=0, raw=None...9, start_col=0, end_line=899, end_col=0, raw=None), delimiters={}), config=ParserConfig(non_int_type=<class 'float'>))}
        prefer_resource_as_file = True
        source_location = PosixPath('/opt/hostedtoolcache/Python/3.10.14/x64/lib/python3.10/site-packages/pint/default_en.txt')
        spec       = <class 'pint.delegates.txt_defparser.defparser._PintParser'>
        strip_spaces = True
        target     = 'constants_en.txt'
/opt/hostedtoolcache/Python/3.10.14/x64/lib/python3.10/site-packages/flexparser/flexparser.py:1182: in parse
    return self.parse_file(source_location)
        self       = <pint.delegates.txt_defparser.defparser._PintParser object at 0x7fb9547fa410>
        source_location = PosixPath('/opt/hostedtoolcache/Python/3.10.14/x64/lib/python3.10/site-packages/pint/constants_en.txt')
/opt/hostedtoolcache/Python/3.10.14/x64/lib/python3.10/site-packages/pint/delegates/txt_defparser/defparser.py:58: in parse_file
    content, _basename = self._diskcache.load(path, super().parse_file)
        __class__  = <class 'pint.delegates.txt_defparser.defparser._PintParser'>
        path       = PosixPath('/opt/hostedtoolcache/Python/3.10.14/x64/lib/python3.10/site-packages/pint/constants_en.txt')
        self       = <pint.delegates.txt_defparser.defparser._PintParser object at 0x7fb9547fa410>
/opt/hostedtoolcache/Python/3.10.14/x64/lib/python3.10/site-packages/flexcache/flexcache.py:379: in load
    converted_object = self.rawload(header, cache_path)
        cache_path = PosixPath('/home/runner/.cache/pint/f5162aaf3660e5d66c2f8d5e987428b18449d72e.pickle')
        converter  = <bound method Parser.parse_file of <pint.delegates.txt_defparser.defparser._PintParser object at 0x7fb9547fa410>>
        converter_id = 'parse_file'
        header     = build_disk_cache_class.<locals>.PathHeader(source=PosixPath('/opt/hostedtoolcache/Python/3.10.14/x64/lib/python3.10/si...system='Linux', python_implementation='CPython', python_version='3.10.14', pint_version='0.24.3', non_int_type='float')
        header_class = <class 'pint.delegates.base_defparser.build_disk_cache_class.<locals>.PathHeader'>
        pass_hash  = False
        self       = <pint.delegates.base_defparser.build_disk_cache_class.<locals>.PintDiskCache object at 0x7fb9547c6440>
        source_object = PosixPath('/opt/hostedtoolcache/Python/3.10.14/x64/lib/python3.10/site-packages/pint/constants_en.txt')
/opt/hostedtoolcache/Python/3.10.14/x64/lib/python3.10/site-packages/flexcache/flexcache.py:421: in rawload
    return pickle.load(fi)
E   EOFError: Ran out of input
        cache_path = PosixPath('/home/runner/.cache/pint/f5162aaf3660e5d66c2f8d5e987428b18449d72e.pickle')
        fi         = <_io.BufferedReader name='/home/runner/.cache/pint/f5162aaf3660e5d66c2f8d5e987428b18449d72e.pickle'>
        header     = build_disk_cache_class.<locals>.PathHeader(source=PosixPath('/opt/hostedtoolcache/Python/3.10.14/x64/lib/python3.10/si...system='Linux', python_implementation='CPython', python_version='3.10.14', pint_version='0.24.3', non_int_type='float')
        self       = <pint.delegates.base_defparser.build_disk_cache_class.<locals>.PintDiskCache object at 0x7fb9547c6440>
_____________________________ ERROR collecting gw1 _____________________________
Different tests were collected between gw3 and gw1. The difference is:
--- gw3

+++ gw1

@@ -1,3 +1,17 @@

+tests/test_activity.py::test_units_and_equality
+tests/test_activity.py::test_debye_params
+tests/test_activity.py::test_activity_crc_HCl
+tests/test_activity.py::test_activity_crc_CsI
+tests/test_activity.py::test_activity_crc_bacl2
+tests/test_activity.py::test_activity_crc_licl
+tests/test_activity.py::test_activity_crc_rbcl
+tests/test_activity.py::test_activity_crc_MgCl2
+tests/test_activity.py::test_activity_crc_KBr
+tests/test_activity.py::test_activity_crc_k2so4
+tests/test_activity.py::test_activity_pitzer_nacl_1
+tests/test_activity.py::test_water_activity_pitzer_nacl_1
+tests/test_activity.py::test_activity_pitzer_phreeqc_nacl_2
+tests/test_activity.py::test_water_activity_phreeqc_pitzer_nacl_2
 tests/test_bulk_properties.py::test_empty_solution
 tests/test_bulk_properties.py::test_hardness_1
 tests/test_bulk_properties.py::test_hardness_2
To see why this happens see Known limitations in documentation
_____________________________ ERROR collecting gw0 _____________________________
Different tests were collected between gw3 and gw0. The difference is:
--- gw3

+++ gw0

@@ -1,3 +1,17 @@

+tests/test_activity.py::test_units_and_equality
+tests/test_activity.py::test_debye_params
+tests/test_activity.py::test_activity_crc_HCl
+tests/test_activity.py::test_activity_crc_CsI
+tests/test_activity.py::test_activity_crc_bacl2
+tests/test_activity.py::test_activity_crc_licl
+tests/test_activity.py::test_activity_crc_rbcl
+tests/test_activity.py::test_activity_crc_MgCl2
+tests/test_activity.py::test_activity_crc_KBr
+tests/test_activity.py::test_activity_crc_k2so4
+tests/test_activity.py::test_activity_pitzer_nacl_1
+tests/test_activity.py::test_water_activity_pitzer_nacl_1
+tests/test_activity.py::test_activity_pitzer_phreeqc_nacl_2
+tests/test_activity.py::test_water_activity_phreeqc_pitzer_nacl_2
 tests/test_bulk_properties.py::test_empty_solution
 tests/test_bulk_properties.py::test_hardness_1
 tests/test_bulk_properties.py::test_hardness_2
To see why this happens see Known limitations in documentation

---------- coverage: platform linux, python 3.10.14-final-0 ----------
Coverage XML written to file coverage.xml

=========================== short test summary info ============================
ERROR tests/test_activity.py - EOFError: Ran out of input
ERROR tests/test_activity.py - EOFError: Ran out of input
ERROR gw1
ERROR gw0
rkingsbury commented 2 months ago

@abhardwaj73 - See info above and feel free to investigate if you have time