""" Function related to ASE2SPRKKR examples """
from pathlib import Path
import ast
import textwrap
import re
import shutil
from ase2sprkkr.common.decorators import cached_property
import ase2sprkkr
[docs]
def examples_dir():
""" Base dir for all examples """
return Path(ase2sprkkr.__file__).parent / "examples"
[docs]
class Example:
[docs]
@staticmethod
def by_number(example:int):
""" Directory for the given example """
# Find the matching example directory
prefix = f"A{example:02d}_"
for d in examples_dir().iterdir():
if d.is_dir() and d.name.startswith(prefix):
return Example(d)
raise FileNotFoundError(f"No example directory matching {prefix}* found.")
[docs]
def __init__(self, path: Path):
self.dir = path
[docs]
def copy(self, dest_dir: str):
"""
Copy contents of ase2sprkkr.examples.A{n:02d}_* directory into dest_dir
and return the path to the .py script inside that example directory
(excluding __init__.py).
:param n: Example number (integer)
:param dest_dir: Destination directory where files will be copied
:return: Path to the .py example script
"""
# Create destination directory
dest_dir = Path(dest_dir)
dest_dir.mkdir(parents=True, exist_ok=True)
# Copy contents of example dir → destination
for item in self.dir.iterdir():
dst = dest_dir / item.name
if item.name.startswith('__'):
continue
if item.is_dir():
shutil.copytree(item, dst, dirs_exist_ok=True)
else:
shutil.copy2(item, dst)
@property
def name(self):
return self.dir.name
@cached_property
def main_script(self):
""" Find Python script inside example directory (excluding __init__.py).
Now we suspose, that the first such one is the main.
"""
for p in self.dir.glob("*.py"):
if p.name != "__init__.py":
return p
raise FileNotFoundError("No .py script found inside example directory.")
@cached_property
def docstring(self):
# Read the file content
source = self.source()
# Parse the AST
try:
tree = ast.parse(source)
# Return the module-level docstring
return ast.get_docstring(tree)
except SyntaxError:
return None # in case the file is invalid Python
@property
def short_docstring(self):
def first_sentence(text: str) -> str:
match = re.match(r'^\s*([^.]*\.)', text)
if match:
return match.group(1)
return text.strip()
if self.docstring is None:
return None
out = first_sentence(self.docstring)
out = re.sub(r'\s*\n\s*', ' ', out)
return out
[docs]
def source(self):
return self.main_script.read_text(encoding="utf-8")
[docs]
def body_of_main(self):
pattern = re.compile(
r"^def\s+main\s*\([^)]*\)\s*:\s*\n" # match 'def main(...):'
r"((?:[ \t]+.*\n|\n)+)", # capture indented block
re.MULTILINE
)
code = self.source()
match = pattern.search(code)
if not match:
return None
body = match.group(1)
if not body:
return code
# Dedent the body
return textwrap.dedent(body)
[docs]
def list_of_examples():
"""
Return list of tuples: (subdir_name, docstring of example_main_script)
"""
base_dir = examples_dir()
pattern = re.compile(r"^A\d{2}_") # matches A{number*2}_
return [ Example(d) for d in base_dir.iterdir()
if d.is_dir() and pattern.match(d.name) ]