kim-tools 0.4.2__py3-none-any.whl → 0.4.3__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
kim_tools/__init__.py CHANGED
@@ -1,4 +1,4 @@
1
- __version__ = "0.4.2"
1
+ __version__ = "0.4.3"
2
2
 
3
3
  from .aflow_util import *
4
4
  from .aflow_util import __all__ as aflow_all
@@ -1,4 +1,4 @@
1
- ################################################################################
1
+ ###############################################################################
2
2
  #
3
3
  # CDDL HEADER START
4
4
  #
@@ -30,7 +30,6 @@
30
30
  Helper classes for KIM Test Drivers
31
31
 
32
32
  """
33
- import glob
34
33
  import json
35
34
  import logging
36
35
  import os
@@ -38,6 +37,8 @@ import shutil
38
37
  import tarfile
39
38
  from abc import ABC, abstractmethod
40
39
  from copy import deepcopy
40
+ from fnmatch import fnmatch
41
+ from glob import glob
41
42
  from pathlib import Path
42
43
  from secrets import token_bytes
43
44
  from tempfile import NamedTemporaryFile, TemporaryDirectory
@@ -113,6 +114,21 @@ PROP_SEARCH_PATHS_INFO = (
113
114
  )
114
115
  TOKEN_NAME = "kim-tools.token"
115
116
  TOKENPATH = os.path.join("output", TOKEN_NAME)
117
+ PIPELINE_EXCEPTIONS = ["output/pipeline.*"]
118
+
119
+
120
+ def _glob_with_exceptions(
121
+ pattern: str, exceptions: List[str], recursive: bool = False
122
+ ) -> List[os.PathLike]:
123
+ """
124
+ Return a list of paths that match the glob "pattern" but not
125
+ the glob "exceptions"
126
+ """
127
+ match = glob(pattern, recursive=recursive)
128
+ # If we are willing to make Python 3.14 minimum, can use filterfalse
129
+ for exception in exceptions:
130
+ match = [n for n in match if not fnmatch(n, exception)]
131
+ return match
116
132
 
117
133
 
118
134
  def get_supported_lammps_atom_style(model: str) -> str:
@@ -565,10 +581,18 @@ class KIMTestDriver(ABC):
565
581
  List of files that were written by this class explicitly, that we won't
566
582
  touch when cleaning and backing up the output directory. Specified
567
583
  relative to 'output' directory.
584
+ __files_to_ignore_in_output (List[PathLike]):
585
+ List of globs of files to ignore when handling the output directory.
586
+ By default, this is set to the constant PIPELINE_EXCEPTIONS,
587
+ which contains files that need to be left untouched for the
588
+ OpenKIM pipeline. Top-level dotfiles are always ignored.
568
589
  __token (Optional[bytes]):
569
590
  Token that is written to TOKENPATH upon first evaluation. This
570
591
  is used to check that multiple Test Drivers are not being called
571
592
  concurrently, causing potential conflicts in the output directory
593
+ __times_called (Optional[int]):
594
+ Count of number of times the instance has been __call__'ed,
595
+ for numbering aux file archives
572
596
  """
573
597
 
574
598
  class NonKIMModelError(Exception):
@@ -579,7 +603,10 @@ class KIMTestDriver(ABC):
579
603
  """
580
604
 
581
605
  def __init__(
582
- self, model: Union[str, Calculator], suppr_sm_lmp_log: bool = False
606
+ self,
607
+ model: Union[str, Calculator],
608
+ suppr_sm_lmp_log: bool = False,
609
+ files_to_ignore_in_output: List[str] = PIPELINE_EXCEPTIONS,
583
610
  ) -> None:
584
611
  """
585
612
  Args:
@@ -587,6 +614,9 @@ class KIMTestDriver(ABC):
587
614
  ASE calculator or KIM model name to use
588
615
  suppr_sm_lmp_log:
589
616
  Suppress writing a lammps.log
617
+ files_to_ignore_in_output (List[PathLike]):
618
+ List of globs of files to ignore when handling the output directory.
619
+ Top-level dotfiles are always ignored.
590
620
  """
591
621
  if isinstance(model, Calculator):
592
622
  self.__calc = model
@@ -602,7 +632,9 @@ class KIMTestDriver(ABC):
602
632
 
603
633
  self.__output_property_instances = "[]"
604
634
  self.__files_to_keep_in_output = []
635
+ self.__files_to_ignore_in_output = files_to_ignore_in_output
605
636
  self.__token = None
637
+ self.__times_called = None
606
638
 
607
639
  def _setup(self, material, **kwargs) -> None:
608
640
  """
@@ -617,19 +649,25 @@ class KIMTestDriver(ABC):
617
649
  if self.__token is None:
618
650
  # First time we've called this instance of the class
619
651
  assert len(self.property_instances) == 0
652
+ assert self.__times_called is None
653
+
654
+ self.__times_called = 0
620
655
 
621
656
  os.makedirs("output", exist_ok=True)
622
657
 
623
658
  # Move all top-level non-hidden files and directories
624
659
  # to backup
625
- output_glob = glob.glob("output/*")
660
+ output_glob = _glob_with_exceptions(
661
+ "output/*", self.__files_to_ignore_in_output
662
+ )
626
663
  if len(output_glob) > 0:
627
664
  i = 0
628
665
  while os.path.exists(f"output.{i}"):
629
666
  i += 1
630
667
  output_bak_name = f"output.{i}"
631
668
  msg = (
632
- "'output' directory is non-empty, backing up all "
669
+ "'output' directory has files besides dotfiles and allowed "
670
+ "exceptions, backing up all "
633
671
  f"non-hidden files and directories to {output_bak_name}"
634
672
  )
635
673
  print(msg)
@@ -661,10 +699,13 @@ class KIMTestDriver(ABC):
661
699
  " prevent conflicts in the output directory, this is not "
662
700
  "allowed."
663
701
  )
702
+ self.__times_called += 1
664
703
 
665
704
  # We should have a record of all non-hidden files in output. If any
666
705
  # untracked files are present, raise an error
667
- output_glob = glob.glob("output/**", recursive=True)
706
+ output_glob = _glob_with_exceptions(
707
+ "output/**", self.__files_to_ignore_in_output, True
708
+ )
668
709
  for filepath in output_glob:
669
710
  if os.path.isfile(filepath): # not tracking directories
670
711
  if (
@@ -682,30 +723,38 @@ class KIMTestDriver(ABC):
682
723
  Archive aux files after a run
683
724
  """
684
725
  # Archive untracked files as aux files
685
- i = 0
686
- while f"aux_files.{i}.txz" in self.__files_to_keep_in_output:
687
- assert os.path.isfile(f"output/aux_files.{i}.txz")
688
- i += 1
689
- tar_prefix = f"aux_files.{i}"
690
- output_glob = glob.glob("output/**", recursive=True)
691
- archived_files = [] # For deleting them later
692
- with tarfile.open(f"output/{tar_prefix}.txz", "w:xz") as tar:
693
- for filepath in output_glob:
694
- if os.path.isfile(filepath): # not tracking directories
726
+ tar_prefix = f"aux_files.{self.__times_called}"
727
+ archive_name = f"output/{tar_prefix}.txz"
728
+ assert not os.path.isfile(tar_prefix)
729
+ output_glob = _glob_with_exceptions(
730
+ "output/**", self.__files_to_ignore_in_output, True
731
+ )
732
+ archived_files = [] # For deleting them later, and checking that any exist
733
+ for filepath in output_glob:
734
+ if os.path.isfile(filepath): # not tracking directories
735
+ output_relpath = os.path.relpath(filepath, "output")
736
+ if output_relpath not in self.__files_to_keep_in_output:
737
+ archived_files.append(filepath)
738
+
739
+ if len(archived_files) > 0:
740
+ msg = f"Auxiliary files found after call, archiving them to {archive_name}"
741
+ print(msg)
742
+ logger.info(msg)
743
+
744
+ with tarfile.open(archive_name, "w:xz") as tar:
745
+ for filepath in archived_files:
695
746
  output_relpath = os.path.relpath(filepath, "output")
696
- if output_relpath not in self.__files_to_keep_in_output:
697
- tar.add(
698
- os.path.join(filepath),
699
- os.path.join(tar_prefix, output_relpath),
700
- )
701
- archived_files.append(filepath)
702
- self.__files_to_keep_in_output.append(f"{tar_prefix}.txz")
703
- for filepath in archived_files:
704
- os.remove(filepath)
705
- try:
706
- os.removedirs(os.path.dirname(filepath))
707
- except OSError:
708
- pass # might not be empty yet
747
+ tar.add(
748
+ os.path.join(filepath),
749
+ os.path.join(tar_prefix, output_relpath),
750
+ )
751
+ self.__files_to_keep_in_output.append(f"{tar_prefix}.txz")
752
+ for filepath in archived_files:
753
+ os.remove(filepath)
754
+ try:
755
+ os.removedirs(os.path.dirname(filepath))
756
+ except OSError:
757
+ pass # might not be empty yet
709
758
 
710
759
  # should not have removed output dir in any situation
711
760
  assert os.path.isdir("output")
@@ -722,9 +771,9 @@ class KIMTestDriver(ABC):
722
771
 
723
772
  Main operation of a Test Driver:
724
773
 
774
+ * Call :func:`~KIMTestDriver._init_output_dir`
725
775
  * Run :func:`~KIMTestDriver._setup` (the base class provides a barebones
726
776
  version, derived classes may override)
727
- * Call :func:`~KIMTestDriver._init_output_dir`
728
777
  * Call :func:`~KIMTestDriver._calculate` (implemented by each individual
729
778
  Test Driver)
730
779
  * Call :func:`~KIMTestDriver._archive_aux_files`
@@ -744,14 +793,15 @@ class KIMTestDriver(ABC):
744
793
  # Set up the output directory
745
794
  self._init_output_dir()
746
795
 
747
- # _setup is likely overridden by an derived class
748
- self._setup(material, **kwargs)
749
-
750
- # implemented by each individual Test Driver
751
- self._calculate(**kwargs)
752
-
753
- # Postprocess output directory for this invocation
754
- self._archive_aux_files()
796
+ try:
797
+ # _setup is likely overridden by an derived class
798
+ self._setup(material, **kwargs)
799
+
800
+ # implemented by each individual Test Driver
801
+ self._calculate(**kwargs)
802
+ finally:
803
+ # Postprocess output directory for this invocation
804
+ self._archive_aux_files()
755
805
 
756
806
  # The current invocation returns a Python list of dictionaries containing all
757
807
  # properties computed during this run
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: kim-tools
3
- Version: 0.4.2
3
+ Version: 0.4.3
4
4
  Summary: Base classes and helper routines for writing KIM Tests
5
5
  Author-email: ilia Nikiforov <nikif002@umn.edu>, Ellad Tadmor <tadmor@umn.edu>, Claire Waters <bwaters@umn.edu>, "Daniel S. Karls" <karl0100umn@gmail.com>, Matt Bierbaum <matt.bierbaum@gmail.com>, Eric Fuemmeler <efuemmel@umn.edu>, Philipp Hoellmer <ph2484@nyu.edu>, Guanming Zhang <gz2241@nyu.edu>, Tom Egg <tje3676@nyu.edu>, Navaneeth Mohan <mohan227@umn.edu>
6
6
  Maintainer-email: ilia Nikiforov <nikif002@umn.edu>
@@ -1,4 +1,4 @@
1
- kim_tools/__init__.py,sha256=ATvBQMEr5NBWxGzIDxLFJxMZIXTwxIx5RVS5-OO4H98,433
1
+ kim_tools/__init__.py,sha256=Tw-dFo4IjQt_u8V_veHj6byvwl1KfmazTL1GdoWwcgM,433
2
2
  kim_tools/kimunits.py,sha256=aCouh7z6fQDR1rcbRTO72l-_RuccuznrKrNF18F-cEQ,6076
3
3
  kim_tools/aflow_util/__init__.py,sha256=lJnQ8fZCma80QVRQeKvY4MQ87oCWu-9KATV3dKJfpDc,80
4
4
  kim_tools/aflow_util/core.py,sha256=OmU4-m2jl81jfs0ni4bSECpC8GYt7-hEEH7cfKmrdoM,80886
@@ -2023,11 +2023,11 @@ kim_tools/symmetry_util/data/wyck_pos_xform_under_normalizer.json,sha256=6g1YuYh
2023
2023
  kim_tools/symmetry_util/data/wyckoff_multiplicities.json,sha256=qG2RPBd_-ejDIfz-E4ZhkHyRpIboxRy7oiXkdDf5Eg8,32270
2024
2024
  kim_tools/symmetry_util/data/wyckoff_sets.json,sha256=f5ZpHKDHo6_JWki1b7KUGoYLlhU-44Qikw_-PtbLssw,9248
2025
2025
  kim_tools/test_driver/__init__.py,sha256=KOiceeZNqkfrgZ66CiRiUdniceDrCmmDXQkOw0wXaCQ,92
2026
- kim_tools/test_driver/core.py,sha256=ugPL_wDzKwT7ch7Xh6SFMCLTF1Qf8x405HqcGJ29fd4,106587
2026
+ kim_tools/test_driver/core.py,sha256=MtoJgGWPbqaMG1yOhNZd83Waw5qKwI4jJKkXfP1rMJc,108701
2027
2027
  kim_tools/vc/__init__.py,sha256=zXjhxXCKVMLBMXXWYG3if7VOpBnsFrn_RjVpnohDm5c,74
2028
2028
  kim_tools/vc/core.py,sha256=BIjzEExnQAL2S90a_npptRm3ACqAo4fZBtvTDBMWMdw,13963
2029
- kim_tools-0.4.2.dist-info/licenses/LICENSE.CDDL,sha256=I2luEED_SHjuZ01B4rYG-AF_135amL24JpHvZ1Jhqe8,16373
2030
- kim_tools-0.4.2.dist-info/METADATA,sha256=dCmTQkNmZhR6y-2GtlwbgMnFMh14kgKgTlkvGL4UHpw,2196
2031
- kim_tools-0.4.2.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
2032
- kim_tools-0.4.2.dist-info/top_level.txt,sha256=w_YCpJ5ERigj9te74ln7k64tqj1VumOzM_s9dsalIWY,10
2033
- kim_tools-0.4.2.dist-info/RECORD,,
2029
+ kim_tools-0.4.3.dist-info/licenses/LICENSE.CDDL,sha256=I2luEED_SHjuZ01B4rYG-AF_135amL24JpHvZ1Jhqe8,16373
2030
+ kim_tools-0.4.3.dist-info/METADATA,sha256=8bkatoFLO7BAGC-C6gGwIoCjI4s7HNqNLv-SmM10rZw,2196
2031
+ kim_tools-0.4.3.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
2032
+ kim_tools-0.4.3.dist-info/top_level.txt,sha256=w_YCpJ5ERigj9te74ln7k64tqj1VumOzM_s9dsalIWY,10
2033
+ kim_tools-0.4.3.dist-info/RECORD,,