Source code for curry.toolchain.flat2icurry.interfaces

'''
Finds and reads the FlatCurry interfaces of imported modules.

The Curry front end writes the interface ``M.fint`` of a module ``M`` beside
its ``M.fcy``.  Two layouts occur.  The front end puts the files of the
hierarchical module ``A.B`` under ``<root>/.curry/<subdir>/A/B.fint``, and
Sprite keeps per-directory products under ``<root>/A/.curry/<subdir>/B.fint``.
The finder tries both.  When no interface exists, the full ``.fcy`` serves,
because the translation uses only names, visibilities, and data types.
'''

from . import flatcurry as fc
from .errors import Flat2ICurryError
import os

__all__ = ['InterfaceFinder', 'load_interface', 'module_root', 'product_path']

SUFFIXES = ('.fint', '.fcy')

_LOADED = {}

[docs] def load_interface(filename): '''Reads a FlatCurry file. Files are cached by name, size, and time.''' st = os.stat(filename) key = (filename, st.st_size, st.st_mtime_ns) prog = _LOADED.get(key) if prog is None: prog = _LOADED[key] = fc.load(filename) return prog
[docs] class InterfaceFinder: ''' Looks up the interface of a module by name. Args: searchdirs: The directories to search, as a Curry path. subdirs: The names of the subdirectories of ``.curry`` that hold the front end output, e.g., ``['pakcs-3.4.1']``. progs: Programs already in hand, used before any file is read. ''' def __init__(self, searchdirs, subdirs, progs=()): self.searchdirs = [os.path.abspath(d) for d in searchdirs] self.subdirs = list(subdirs) self.progs = {prog.name: prog for prog in progs}
[docs] def candidates(self, modname): parts = modname.split('.') for searchdir in self.searchdirs: for subdir in self.subdirs: for suffix in SUFFIXES: yield os.path.join(searchdir, '.curry', subdir, *parts) + suffix if len(parts) > 1: yield os.path.join( searchdir, *parts[:-1], '.curry', subdir, parts[-1] + suffix )
[docs] def filename(self, modname): '''The file that holds the interface of ``modname``.''' tried = [] for candidate in self.candidates(modname): if os.path.isfile(candidate): return candidate tried.append(candidate) raise Flat2ICurryError( 'no FlatCurry interface for module %r; tried:\n %s' % (modname, '\n '.join(tried)) )
[docs] def find(self, modname): '''The FlatCurry interface of ``modname``.''' prog = self.progs.get(modname) if prog is None: prog = self.progs[modname] = load_interface(self.filename(modname)) return prog
[docs] def product_path(filename): ''' Splits the path of a front-end product into ``(root, subdir, tail)``. The file lies at ``<root>/.curry/<subdir>/<tail>/M.ext``; ``tail`` is empty for a module at the top level and ``A/B`` for a module under ``A.B``. Returns None when no ``.curry`` directory encloses the file. ''' outdir = os.path.dirname(os.path.abspath(filename)) tail = [] while True: parent, name = os.path.split(outdir) if not name or parent == outdir: return None if os.path.basename(parent) == '.curry': return os.path.dirname(parent), name, os.path.join(*tail) if tail else '' tail.insert(0, name) outdir = parent
[docs] def module_root(fcyfile, modname): ''' The directory from which the module ``modname`` was compiled, given its FlatCurry file. The file of module ``A.B`` lies at ``<root>/.curry/<subdir>/A/B.fcy`` or at ``<root>/A/.curry/<subdir>/B.fcy``. ''' outdir = os.path.dirname(os.path.abspath(fcyfile)) parts = modname.split('.')[:-1] tail = os.path.join(*parts) if parts else '' if tail and outdir.endswith(os.sep + tail): outdir = outdir[:-len(tail) - 1] return os.path.dirname(os.path.dirname(outdir)) root = os.path.dirname(os.path.dirname(outdir)) if tail and root.endswith(os.sep + tail): root = root[:-len(tail) - 1] return root