- pylightnix.types
- pylightnix.core
- PYLIGHTNIX_STORE_VERSION
- PYLIGHTNIX_NAMEPAT
- PYLIGHTNIX_RESERVED
- logger
- info
- warning
- TL
- tlregistry
- tlstorage
- storagename
- fsroot
- fstmpdir
- fsstorage
- assert_valid_storage
- mkSS
- setstorage
- setregistry
- fsinit
- reserved
- trimhash
- mkdref
- rref2dref
- undref
- mkrref
- unrref
- mkname
- path2dref
- path2rref
- mkconfig
- cfgdict
- cfgcattrs
- cfgserialize
- cfghash
- cfgname
- cfgdeps
- mkrefpath
- resolve
- dref2path
- rref2path
- rrefpath2path
- drefcfgpath
- rrefctx
- drefcfg_
- drefcfg
- drefattrs
- rrefattrs
- drefdeps1
- rrefdeps1
- drefdeps
- rrefdeps
- alldrefs
- allrrefs
- rootdrefs
- rootrrefs
- rrefdata
- drefrrefs
- drefrrefsC
- store_gc
- mkdrv_
- mkrealization
- mkcontext
- context_eq
- context_add
- context_deref
- context_derefpath
- context_serialize
- output_validate
- output_realizer
- output_matcher
- mkdrv
- current_registry
- current_storage
- mkclosure
- _A
- instantiate
- RealizeSeqGen
- realize1
- realizeMany
- realize
- realizeSeq
- evaluate
- Key
- texthash
- latest
- exact
- match
- match_all
- match_some
- match_only
- match_latest
- match_exact
- cfgsp
- assert_valid_refpath
- assert_valid_config
- assert_valid_name
- assert_valid_rref
- assert_valid_hashpart
- assert_valid_dref
- assert_valid_hash
- assert_valid_context
- assert_valid_closure
- assert_rref_deps
- assert_have_realizers
- pylightnix.build
- logger
- info
- warning
- error
- BuildError
- mkbuildargs
- _B
- build_wrapper_
- build_wrapper
- build_config
- build_context
- build_cattrs
- build_markstart
- build_markstop
- build_markstop_noexcept
- rrefbstart
- rrefbstop
- rrefbdelta
- build_outpaths
- build_outpath
- build_name
- build_deref_
- build_deref
- build_paths
- build_path
- build_environ
- repl_continueBuild
- repl_buildargs
- repl_build
- repl_cancelBuild
- pylightnix.repl
- pylightnix.stages
- pylightnix.stages.trivial
- pylightnix.stages.fetch2
- pylightnix.stages.fetch
- pylightnix.bashlike
- pylightnix.lens
- pylightnix.either
- pylightnix.arch
- pylightnix.deco
All main types which we use in Pylightnix are defined here.
Path
is an alias for string. It is used in pylightnix to
tell the typechecker that a given string contains a filesystem path.
SPath
is an alias for string. It is used in pylightnix to
tell the typechecker that a given string contains a path to storage.
StorageSettings = NamedTuple('StorageSettings',[('root',Optional[Path]),
...
Stoarge settings contains a path for the main stoarge and a path for temporary directories. These paths need to be on the same device in order to atomic rename work.
Hash
is an alias for string. It is used in pylightnix to
tell the typechecker that a given string contains sha256 hash digest.
HashPart
is an alias for string. It is used in pylightnix to
tell the typechecker that a given string contains first 32 characters of
sha256 hash digest.
DRef
stands for derivation reference. It is a string identifier of a
filesystem part of Derivation object.
The format of derivation reference is <HashPart>-<Name>
, where:
<HashPart>
contains first 32 characters of derivationRConfig
's sha256 hash digest.<Name>
object contains the name of derivation.
Derivation reference 'points to' derivation object in pylightnix filesystem
storage. For a valid DRef, $PYLIGHTNIX_STORE/<HashPart>-<Name>/
does
exist and is a directory which contains config.json
file.
Derivation references are results of instantiation.
Derivation reference may be converted into a realization reference by either dereferencing (that is by querying for existing realizations) or by realizing it from scratch.
- For derefencing dependencies at the build time, see build_deref.
- For querying the storage, see store_deref.
RRef
stands for Realization Reference. RRefs identify collections of
artifacts of a Stage. Stages with non-determenistic
realizers may have several competing realization instances. Every such
instance is identified by a unique RRef.
The format of realization reference is <HashPart0>-<HashPart1>-<Name>
,
where:
<HashPart0>
is calculated over realization's Context and build artifacts.<HashPart1>-<Name>
forms valid DRef which this realizaion was realized from.
Realization reference is created during the realization process .
Valid realization references may be dereferenced down to system paths of build artifacts by calling rref2path or by using lenses.
Autostage decorator unwraps RRefs of parent stages into Attrs objects.
Name
is an alias for string. It is used in pylightnix to tell the
typechecker that a given string contains name of a pylightnix storage object.
Names are restircted to contain charaters matching PYLIGHTNIX_NAMEPAT
.
See also mkname
RefPath = List[Union[DRef,str]]
RefPath is an alias for Python list (of strings). The first item of
RefPath
is a derivation reference. Other
elements are to represent parts of file path.
RefPath is designed to be used in a stage config where they typically refer
to artifacts of already existing dependencies. To refer to future artifacts of
the derivation being configured, use
PromisePaths.
To convert RefPath
into a system path, one
generally have to perform the following basic actions:
- Dereference it's first item to obtain the realization. See store_deref or build_deref.
- Convert the realization reference into system path with rref2path
- Join the system path with
[1:]
part of RefPath to get the real filename.
The algorithm described above is implemented as build_path helper function.
Base class of Pylightnix exceptions
def __init__(self, dref: DRef, failed: List[Tuple[Path,RefPath]])
def __init__(self, dref: DRef, failed: List[Tuple[Path,RefPath]])
_REF = TypeVar('_REF')
def __init__(self, val: Iterable[_REF])
Output is a base class for 'organized collections of realizations', either in form of temporary Paths or RRefs.
TODO: Rename into something which has a meaning of PromisedOuput
def __init__(self, val: Iterable[_REF])
Context = Dict[DRef,List[RRef]]
InstantiateArg = Dict[str,Any]
Type of user-defined arguments to pass to the Config
RealizeArg = Dict[str,Any]
Type of user-defined arguments to pass to the Realizer
Matcher = Callable[[Optional[StorageSettings],List[RRef]],
Optional[List[RRef]]]
Matchers are user-defined Python functions with the fixed signature. They serve two purposes:
- Decide whether to launch a new realization or re-use the results of realizations completed earlier.
- Filter a subset of a realizations to depend on out of the set of available realizations.
Matchers answer 'yes' to the first question by returning None. Non-none value specifies the matched set of realizations.
The Matcher's invariants are:
- Matcher outputs should only depend on the immutable realizations passed to them as inputs. Matchers should avoid having side-effects.
- Matchers must be satisfiable. If the matcher returns None, the core re-runs runs the realization and calls the matcher once again. Returning None again would be an error.
Pylightnix includes a set of built-in matchers:
- match_latest prefers the latest realizations
- match_all takes everything
- match_some takes not less than N realizations
- match_only expects exactly one realization
MatcherO = Callable[[Optional[StorageSettings],Output[RRef]],
Optional[Output[RRef]]]
Realizer = Callable[[Optional[StorageSettings],DRef,Context,RealizeArg],List[Path]]
Realizers are user-defined Python functions. Realizers typically implement application-specific algorithms which take some configuration parameters and produce some artifacts.
Realizer accepts the following arguments:
- Path to a global Pylightnix storage
- A Derivation reference being built
- A Context encoding the results of dependency resolution.
- Set of additional user-defined arguments
Context is the key to accessing the dependency artifacts.
Derivation reference is required to access configuration parameters of the algorithm.
Realizers must return one or many folders of realization artifacts (files and
folders containing application-specific data). Every folder is treated as an
alternative realization. Matcher is later used
to pick the subset of realizations which matches some application-specific
criteria. This subset will eventually appear as the Context
s of downstream
realizaions.
Pylightnix stages may use the simplified realizer API provided by the Build helper class.
Example:
def mystage(r:Registry)->DRef:
def _realize(dref:DRef, context:Context)->List[Path]:
b=mkbuild(dref, context, buildtime=buildtime)
with open(join(build_outpath(b),'artifact'),'w') as f:
f.write('chickenpoop\n')
return [build_outpath(b)]
...
return mkdrv(r, ..., _realize)
RealizerO = Callable[[Optional[StorageSettings],DRef,Context,RealizeArg],Output[Path]]
Derivation = NamedTuple('Derivation', [('dref',DRef),
('matcher',Matcher), ...
Derivation is a core Pylightnix entity. It holds the information required to produce artifacts of individual Stage.
Fields include:
- Configuration objects serialized on disk.
- Matcher Python function
- Realizer Python function
The actual configuration is stored in the Pylightnix filesystem storage. Derivation holds the DRef access key.
Derivations normally appear as a result of mkdrv calls.
Closure = NamedTuple('Closure', [('result',Any),
('targets',List[DRef]),
...
Closure describes the build plan for one or many Derivations.
Closures are typically obtained as a result of the instantiate and are consumed by the call to realize or it's analogs.
def __init__(self, d: dict)
Config is a JSON-serializable dict-like object containing user-defined attributes. Together with Realizers and Matchers, configs describe Stage objects.
Configs carry Python dictionaries that should contain JSON-serializable types.
Strings, bools, ints, floats, lists or other dicts are fine, but no bytes,
numpy.float32
or lambdas are allowed. Tuples are also forbidden because they
are not preserved (decoded into lists). Special emphasis is placed on
DRef support which link dependent stages together.
Config of a derivation can't include the Derivation reference to itself, because it contains the config hash as its part.
Some field names of a config have a special meaning for Pylightnix:
- String
name
field will be used as a part of references to a derivation associated with this config. - RefPaths represent paths to artifacts within the stage artifact folders.
- SelfRef paths represent output paths to be produced during the stage's realization.
- DRef represent stage dependencies. Pylightnix collects derivation references and plan the realization order based on them.
Storing an RRef in the config leads to a warning. Pylightnix does not necessarily knows how to produce the exact reference, so the end result may not match the expectations.
Configs are normally created from Python dicts by the mkconfig function.
Example:
def mystage(r:Registry)->Dref:
def _config()->dict:
name = 'mystage'
nepoches = 4
learning_rate = 1e-5
hidden_size = 128
return locals()
return mkdrv(mkconfig(_config()),...)
def __init__(self, d: dict)
def __repr__(self) -> str
RConfig
is a Config where all
Self-referenes are resolved. RConfig
stands for 'Resolved Config'.
def __init__(self, d: dict)
ConfigAttrs
is a helper object allowing to access
RConfig fields as Python object attributes.
DEPRECATED in favour of Lenses.
def __init__(self, d: dict)
BuildArgs = NamedTuple('BuildArgs', [('S',Optional[StorageSettings]),
('dre ...
def __init__(self, ba: BuildArgs) -> None
Build objects track the process of stage's realization. Build allows users to define Realizers with only a simple one-argument signature. The build_wrapper function converts simplified Build-realizers into the regular ones.
Typical Build operations include:
- build_config - Obtain the RConfig object of the current stage
- build_cattrs - Obtain the ConfigAttrs helper
- build_path - Convert a RefPath or a self-ref path into a system file path
- build_setoutgroups - Initialize and return groups of output folders
- build_deref - Convert a dependency DRef into a realization reference.
Lenses accept Build
objects as a configuration
source for derivations being realized.
Build class may be subclassed by applications in order to define
application-specific build-state. Underscoped
build_wrapper_ accepts additional callback
parameter which informs the core what subclass to create. Note that derived
classes should have the same constructor def __init__(self, ba:BuildArgs)->None
.
Example:
class TensorFlowModel(Build):
model:tf.keras.Model
def train(r:TensorFlowModel)->None:
o = build_outpath(r)
r.model = create_model(...)
...
def mymodel(r:Registry)->DRef:
return mkdrv(r, ..., build_wrapper_(TensorFlowModel, train))
def __init__(self, ba: BuildArgs) -> None
def __init__(self, S: Optional[StorageSettings] = None)
The derivation registry is a mutable storage object where Pylightnix stores derivations before combining them into a Closure.
Registry doesn't requre any special operations besides creating and passing
around. By convention, Registry objects are first arguments of user-defined
stage functions and the mkdrv
API function of Pylightnix.
def __init__(self, S: Optional[StorageSettings] = None)
DRefLike = TypeVar('DRefLike',bound=DRef)
StageResult = Union[DRef,List[DRef],Dict[Any,DRef],Tuple[DRef,...]]
Stage = Callable[...,StageResult]
Pylightnix Stage
is a signature of a Python function which creates a
Derivations object and registers it in a
Registry.
Stages are the top-level building blocks of Pylightnix.
Normally, stage functions create derivations by calling mkdrv one or many times and returning a StageResult value which is a name for a container holding one or many DRefs
A stage A
may be set to depend on another stage B
by including the
DRefs of B
into the configuration part of the
A
's derivation.
In order to run the registered stage(s), user has to pass the collectoin of DRefs to be realized to instantiate followed by the call to realize functions.
autostage decorator turns a Python function into a Pylightnix stage.
Pylightnix defines a number of built-in stages:
A note on typing: Real stages often accept additional custom arguments which AFAIK couldn't be handled by the simple MyPy. In a somewhat extended MyPy the Stage definition would look like:
Stage = Callable[[Registry,VarArg(Any),KwArg(Any)],StageResult]
Stage's return value is a derivation reference which could be either used in other stages, or instantiated into the stage realization plan.
Core Pylightnix definitions
PYLIGHTNIX_STORE_VERSION = 0
Do not change! Tracks the version of pylightnix storage
PYLIGHTNIX_NAMEPAT = "[a-zA-Z0-9_-]"
Set the regular expression pattern for valid name characters.
PYLIGHTNIX_RESERVED = ['context.json','group.json']
Reserved file names are treated specially be the core. Users should not normally create or alter files with these names.
logger = getLogger(__name__)
info = logger.info
warning = logger.warning
TL = threading_local()
Thread-local storage for current_registry to store its state.
def tlregistry(M: Optional[Registry]) -> Optional[Registry]
Return the currently active Registry
def tlstorage(S: Optional[StorageSettings]) -> Optional[StorageSettings]
Return the currently active StorageSettings
def storagename()
Return the name of Pylightnix storage filder.
def fsroot(S: Optional[StorageSettings] = None) -> Path
fsroot
contains the path to the root of pylightnix shared data folder.
Default is ~/_pylightnix
or /var/run/_pylightnix
if no $HOME
is
available. Setting PYLIGHTNIX_ROOT
environment variable overwrites the
defaults.
def fstmpdir(S: Optional[StorageSettings] = None) -> Path
Return the location of current Pylightnix temporary folder, defaulting to the path set by PYLIGHTNIX_TMP environment variable.
def fsstorage(S: Optional[StorageSettings] = None) -> Path
Return the location of current Pylightnix storage folder, defaulting to the path set by PYLIGHTNIX_STORAGE environment variable.
def assert_valid_storage(S: Optional[StorageSettings] = None) -> None
def mkSS(root: str, stordir: Optional[str] = None, tmpdir: Optional[str] = None) -> StorageSettings
Constructor for StorageSettings
def setstorage(S: Optional[StorageSettings]) -> Optional[StorageSettings]
def setregistry(r: Optional[Registry]) -> Optional[Registry]
def fsinit(ss: Optional[Union[str,StorageSettings]] = None, check_not_exist: bool = False, remove_existing: bool = False, use_as_default: bool = False) -> None
Imperatively create the filesystem storage and temp direcory if they don't
exist. Default locations may be altered by PYLIGHTNIX_STORAGE
and
PYLIGHTNIX_TMP
env variables.
def reserved(folder: Path, name: str) -> Path
def trimhash(h: Hash) -> HashPart
Trim a hash to get HashPart
objects which are used in referencing
def mkdref(dhash: HashPart, refname: Name) -> DRef
def rref2dref(rref: RRef) -> DRef
def undref(r: DRef) -> Tuple[HashPart, Name]
def mkrref(rhash: HashPart, dhash: HashPart, refname: Name) -> RRef
def unrref(r: RRef) -> Tuple[HashPart, HashPart, Name]
def mkname(s: str) -> Name
def path2dref(p: Path) -> Optional[DRef]
Takes either a system path of some realization in the Pylightnix storage
or a symlink pointing to such path. Return a DRef
which corresponds to this
path.
Note: path2dref
operates on p
symbolically. It doesn't actually check the
presence of such an object in storage
def path2rref(p: Path) -> Optional[RRef]
Takes either a system path of some realization in the Pylightnix storage
or a symlink pointing to such path. Return RRef
which corresponds to this
path.
Note: path2rref
operates on p
symbolically. It doesn't actually check the
presence of such an object in storage
def mkconfig(d: dict) -> Config
Create a Config object out of config
dictionary. Asserts if the dictionary is not JSON-compatible. As a handy hack,
filter out r:Registry
variable which likely is an utility
Registry object.
def cfgdict(cp: Config) -> dict
def cfgcattrs(c: RConfig) -> Any
def cfgserialize(c: Config) -> str
def cfghash(c: Config) -> Hash
def cfgname(c: Config) -> Name
Return a name
field of a config c
, defaulting to string "unnmaed".
def cfgdeps(c: Config) -> Set[DRef]
def mkrefpath(r: DRef, items: List[str] = []) -> RefPath
Construct a RefPath out of a reference ref
and a path within the stage's realization
def resolve(c: Config, r: DRef) -> RConfig
Replace all Promise tags with DRef r
. In particular, all PromisePaths
are converted into RefPaths.
def dref2path(r: DRef, S=None) -> Path
def rref2path(r: RRef, S=None) -> Path
def rrefpath2path(r: RRef, refpath: RefPath, S=None) -> Path
def drefcfgpath(r: DRef, S=None) -> Path
def rrefctx(r: RRef, S=None) -> Context
Return the realization context.
def drefcfg_(dref: DRef, S=None) -> Config
Return dref
configuration, selfrefs are not resolved
def drefcfg(dref: DRef, S=None) -> RConfig
Return dref
configuration, selfrefs are resolved
def drefattrs(r: DRef, S=None) -> Any
Read the ConfigAttrs of the storage node r
.
Note, that it is a kind of 'syntactic sugar' for drefcfg
. Both
functions do the same thing.
def rrefattrs(r: RRef, S=None) -> Any
Read the ConfigAttrs of the storage node r
.
Note, that it is a kind of 'syntactic sugar' for drefcfg
. Both
functions do the same thing.
def drefdeps1(drefs: Iterable[DRef], S=None) -> Set[DRef]
Return a set of reference's immediate dependencies, not including drefs
themselves.
def rrefdeps1(rrefs: Iterable[RRef], S=None) -> Set[RRef]
Return a set of reference's immediate dependencies, not including rrefs
themselves.
def drefdeps(drefs: Iterable[DRef], S=None) -> Set[DRef]
Return the complete set of drefs
's dependencies, not including drefs
themselves.
def rrefdeps(rrefs: Iterable[RRef], S=None) -> Set[RRef]
Return the complete set of rrefs's dependencies, not including rrefs
themselves.
TODO: Validate the property that the resulting set IS the minimal complete
set of RRef dependencies. Now it looks so only by creation (see realizeSeq
,
line mark I
)
def alldrefs(S=None) -> Iterable[DRef]
Iterates over all derivations of the storage located at S
(PYLIGHTNIX_STORE env is used by default)
def allrrefs(S=None) -> Iterable[RRef]
Iterates over all realization references in S
(PYLIGHTNIX_STORE env is
used by default)
def rootdrefs(S: Optional[StorageSettings] = None) -> Set[DRef]
Return root DRefs of the storage S
as a set
def rootrrefs(S: Optional[StorageSettings] = None) -> Set[RRef]
Return root RRefs of the storage S
as a set
def rrefdata(rref: RRef, S=None) -> Iterable[Path]
Iterate over top-level artifacts paths, ignoring reserved files.
def drefrrefs(dref: DRef, S=None) -> Set[RRef]
Iterate over all realizations of a derivation dref
. The sort order is
unspecified. Matchers are not taken into account.
def drefrrefsC(dref: DRef, context: Context, S=None) -> Iterable[RRef]
Iterate over realizations of a derivation dref
that match the specified
context. Sorting order is unspecified.
def store_gc(keep_drefs: List[DRef], keep_rrefs: List[RRef], S: Optional[StorageSettings] = None) -> Tuple[Set[DRef],Set[RRef]]
Take roots which are in use and should not be removed. Return roots which are not used and may be removed. Actual removing is to be done by the user.
Default location of S
may be changed.
See also rmref
def mkdrv_(c: Config, S=None) -> DRef
See mkdrv
def mkrealization(dref: DRef, l: Context, o: Path, S=None) -> RRef
Inserts the newly-obtaind Stage artifacts into the Storage, return the realization reference. Not intended to be called by user.
Parameters:
dref:DRef
: Derivation reference to create the realization of.l:Context
: Context which stores dependency information.o:Path
: Path to temporal (build) folder which contains artifacts, prepared by the Realizer.leader
: Tag name and Group identifier of the Group leader. By default, we use nameout
and derivation's own rref.
def mkcontext() -> Context
def context_eq(a: Context, b: Context) -> bool
def context_add(ctx: Context, dref: DRef, rrefs: List[RRef]) -> Context
Add a pair (dref,rrefs)
into a context ctx
. rrefs
are supposed to
form (a subset of) the realizations of dref
.
Return a new context.
def context_deref(context: Context, dref: DRef) -> List[RRef]
TODO: Should it return Output (aka UniformList
) rather than Python list?
def context_derefpath(context: Context, refpath: RefPath, S=None) -> List[Path]
def context_serialize(c: Context) -> str
def output_validate(dref: DRef, o: Output[Path], S=None) -> List[Path]
def output_realizer(f: RealizerO) -> Realizer
def output_matcher(r: MatcherO) -> Matcher
def mkdrv(config: Config, matcher: Matcher, realizer: Realizer, r: Optional[Registry] = None) -> DRef
Construct a Derivation object out of Config, Matcher and Realizer. Register the derivation in the dependency-resolution Registry. Return Derivation references of the newly-obtained derivation.
Arguments:
r:Registry
: A Registry to update with a new derivation
Example:
def somestage(r:Registry)->DRef:
def _realizer(b:Build):
with open(join(build_outpath(b),'artifact'),'w') as f:
f.write(...)
return mkdrv(r,mkconfig({'name':'mystage'}), match_only(), build_wrapper(_realizer))
rref:RRef=realize1(instantiate(somestage))
@contextmanager
def current_registry(r: Registry) -> Iterator[Registry]
Sets the default global Registry for the inner scoped code. Internally calls current_storage(r.S)
@contextmanager
def current_storage(S: Optional[StorageSettings]) -> Iterator[Optional[StorageSettings]]
Sets the global default StorageSettings for the inner scoped code
def mkclosure(result: Any, r: Registry) -> Closure
_A = TypeVar('_A')
def instantiate(stage: Union[_A,Callable[...,Any]], args: Any, *,, ,, =, ,, =, ,, kwargs: Any) -> Tuple[_A,Closure]
Scans a Python DRef container (list, dict alike) or evaluates the Stage function by calling it.
Returns the Closure formed out of nested Derivations. The closure returned is ready to be realized.
Arguments:
stage
:Stage
function to call orDRef
container to scan.r:Optional[Registry]=None
: Registry to register derivations in.S:Optional[StorageSettings]=None
: StorageSettings specifies the locations of the on-disk derivation data folders. Should matchr.S
ifr
is passed.
Returns:
DRef
container holding references to the newly registered derivations.
RealizeSeqGen = Generator[
Tuple[Optional[StorageSettings],DRef,Context,Derivation,RealizeArg],
Tuple[Optional[L ...
def realize1(closure: Union[Closure,Tuple[StageResult,Closure]], force_rebuild: Union[List[DRef],bool] = [], assert_realized: List[DRef] = [], realize_args: Dict[DRef,RealizeArg] = {}) -> RRef
Realize a closure, assuming that it returns a single realization.
def realizeMany(closure: Union[Closure,Tuple[StageResult,Closure]], force_rebuild: Union[List[DRef],bool] = [], assert_realized: List[DRef] = [], realize_args: Dict[DRef,RealizeArg] = {}) -> List[RRef]
Realize a closure, assuming that it returns a list of realizations.
def realize(closure: Union[Closure,Tuple[StageResult,Closure]], force_rebuild: Union[List[DRef],bool] = [], assert_realized: List[DRef] = [], realize_args: Dict[DRef,RealizeArg] = {}) -> Tuple[StageResult,Closure,Context]
Takes the instantiated Closure and evaluates its targets. Calls the realizers if derivation matchers require so.
Returns the target DRefs, their closure, and the resulting Context.
See also realize1, realizeMany, repl_realize
Example:
def mystage(r:Registry)->DRef:
...
return mkdrv(r, ...)
rrefs=realize1(instantiate(mystage))
print(mklen(rref).syspath)
def realizeSeq(closure: Closure, force_interrupt: List[DRef] = [], assert_realized: List[DRef] = [], realize_args: Dict[DRef,RealizeArg] = {}) -> RealizeSeqGen
realizeSeq
encodes low-level details of the realization algorithm.
Sequentially realize the closure by issuing steps via Python's generator
interface. Consider calling realize or it's
analogs instead.
FIXME: try to implement assert_realized
by calling redefine
with
appropriate failing realizer on every Derivation.
def evaluate(stage, args, *,, ,, kwargs) -> RRef
Key = Callable[[Optional[StorageSettings], RRef],Optional[Union[int,float,str]]]
def texthash() -> Key
def latest() -> Key
def exact(expected: List[RRef]) -> Key
def match(key: Key, trim: Callable[[List[RRef]],Optional[List[RRef]]], mnext: Optional[Matcher] = None) -> Matcher
Create a Matcher by combining different sorting keys and selecting a top-n threshold.
Only realizations which have tag 'out' (which is a default tag name) participate in matching. After the matching, Pylightnix adds all non-'out' realizations which share group with at least one matched realization.
Arguments:
keys
: List of Key functions. Defaults ot
def match_all(S, rrefs)
def match_some(n: int = 1, key=None)
def match_only()
def match_latest(n: int = 1) -> Matcher
def match_exact(rrefs: List[RRef])
def cfgsp(c: Config) -> List[Tuple[str,RefPath]]
Returns the list of self-references (aka self-paths) in the config.
def assert_valid_refpath(refpath: RefPath) -> None
def assert_valid_config(c: Config) -> Config
def assert_valid_name(s: Name) -> None
def assert_valid_rref(ref: str) -> None
def assert_valid_hashpart(hp: HashPart) -> None
def assert_valid_dref(ref: str) -> None
def assert_valid_hash(h: Hash) -> None
Asserts if it's Hash
argument is ill-formed.
def assert_valid_context(c: Context) -> None
def assert_valid_closure(closure: Closure) -> None
def assert_rref_deps(c: Config) -> None
def assert_have_realizers(r: Registry, drefs: List[DRef]) -> None
Built-in realization wrapper named Build
provides helpful functions like
temporary build directory management, time counting, etc.
logger = getLogger(__name__)
info = logger.info
warning = logger.warning
error = logger.error
def __init__(self, S: Optional[StorageSettings], dref: DRef, outpaths: Optional[Output[Path]], exception: Exception, msg: str = '')
Exception class for build errors
def __init__(self, S: Optional[StorageSettings], dref: DRef, outpaths: Optional[Output[Path]], exception: Exception, msg: str = '')
Initialize BuildError instance.
def __str__(self)
def mkbuildargs(S: Optional[StorageSettings], dref: DRef, context: Context, starttime: Optional[str], stoptime: Optional[str], iarg: InstantiateArg, rarg: RealizeArg) -> BuildArgs
_B = TypeVar('_B', bound=Build)
def build_wrapper_(f: Callable[[_B],None], ctr: Callable[[BuildArgs],_B], nouts: Optional[int] = 1, starttime: Optional[str] = 'AUTO', stoptime: Optional[str] = 'AUTO') -> Realizer
Build Adapter which convers user-defined realizers which use Build API into a low-level Realizer
def build_wrapper(f: Callable[[Build],None], nouts: Optional[int] = 1, starttime: Optional[str] = 'AUTO', stoptime: Optional[str] = 'AUTO') -> Realizer
Build Adapter which convers user-defined realizers which use Build API into a low-level Realizer
def build_config(b: Build) -> RConfig
Return the Config object of the realization being built.
def build_context(b: Build) -> Context
Return the Context object of the realization being built.
def build_cattrs(b: Build) -> Any
Cache and return ConfigAttrs
. Cache allows realizers to update it's
value during the build process, e.g. to use it as a storage.
def build_markstart(b: Build, nouts: int) -> List[Path]
def build_markstop(b: Build) -> None
def build_markstop_noexcept(b: Build) -> None
def rrefbstart(rref: RRef, S=None) -> Optional[str]
Return the buildtime of the current RRef in a format specified by the PYLIGHTNIX_TIME constant.
parsetime may be used to parse stings into UNIX-Epoch seconds.
Buildtime is the time when the realization process was started. Some realizations may not provide this information.
def rrefbstop(rref: RRef, S=None) -> Optional[str]
Return the buildtime of the current RRef in a format specified by the PYLIGHTNIX_TIME constant.
parsetime may be used to parse stings into UNIX-Epoch seconds.
Buildtime is the time when the realization process was started. Some realizations may not provide this information.
def rrefbdelta(rref: RRef, S=None) -> Optional[float]
def build_outpaths(b: Build) -> List[Path]
def build_outpath(b: Build) -> Path
Return the output path of the realization being built. Output path is a path to valid temporary folder where user may put various build artifacts. Later this folder becomes a realization.
def build_name(b: Build) -> Name
Return the name of a derivation being built.
def build_deref_(b: Build, dref: DRef) -> List[RRef]
For any realization process described with
it's Build handler, build_deref
queries a
realization of dependency dref
.
build_deref
is designed to be called from
Realizer functions. In other cases,
store_deref should be used.
def build_deref(b: Build, dref: DRef) -> RRef
def build_paths(b: Build, refpath: RefPath) -> List[Path]
def build_path(b: Build, refpath: RefPath) -> Path
A single-realization version of the build_paths.
def build_environ(b: Build, env: Optional[Any] = None) -> dict
Prepare environment by adding Build's config to the environment as variables. The function resolves all singular RefPaths into system paths using current Build's context.
FIXME: Use bash-array syntax for multi-ouput paths
def repl_continueBuild(b: Build, rh: Optional[ReplHelper] = None) -> Optional[RRef]
def repl_buildargs(rh: Optional[ReplHelper] = None) -> BuildArgs
def repl_build(rh: Optional[ReplHelper] = None, nouts: Optional[int] = 1) -> Build
Return Build
object for using in repl-based debugging
Example:
from stages import some_stage, some_stage_build, some_stage_train
rh=repl_realize(instantiate(some_stage))
b=repl_build(rh)
some_stage_build(b) # Debug as needed
some_stage_train(b) # Debug as needed
def repl_cancelBuild(b: Build, rh: Optional[ReplHelper] = None) -> None
Repl module defines variants of instantiate
and realize1
functions, which
are suitable for REPL shells. Repl-friendly wrappers (see repl_realize
) could
pause the computation, save the Pylightnix state into a variable and return to
the REPL's main loop. At this point user could alter the state of the whole
system. Finally, repl_continue
or repl_cancel
could be called to either
continue or cancel the realization.
def __init__(self, gen: RealizeSeqGen) -> None
def __init__(self, gen: RealizeSeqGen) -> None
ERR_INVALID_RH = "Neither global, nor user-defined ReplHelper is valid"
ERR_INACTIVE_RH = "REPL session is not paused or was already unpaused"
def repl_continueAll(out_paths: Optional[List[Path]] = None, out_rrefs: Optional[List[RRef]] = None, rh: Optional[ReplHelper] = None) -> Optional[Context]
def repl_continueMany(out_paths: Optional[List[Path]] = None, out_rrefs: Optional[List[RRef]] = None, rh: Optional[ReplHelper] = None) -> Optional[List[RRef]]
def repl_continue(out_paths: Optional[List[Path]] = None, out_rrefs: Optional[List[RRef]] = None, rh: Optional[ReplHelper] = None) -> Optional[RRef]
def repl_realize(closure: Union[Closure,Tuple[Any,Closure]], force_interrupt: Union[List[DRef],bool] = True, realize_args: Dict[DRef,RealizeArg] = {}) -> ReplHelper
TODO
Example:
rh=repl_realize(instantiate(mystage), force_interrupt=True)
# ^^^ `repl_realize` returnes the `ReplHelper` object which holds the state of
# incomplete realization
b:Build=repl_build()
# ^^^ Access it's build object. Now we may think that we are inside the
# realization function. Lets do some hacks.
with open(join(build_outpath(b),'artifact.txt'), 'w') as f:
f.write("Fooo")
repl_continueBuild(b)
rref=repl_rref(rh)
# ^^^ Since we didn't program any other pasues, we should get the usual RRef
# holding the result of our hacks.
def repl_result(rh: ReplHelper) -> Optional[Context]
def repl_rrefs(rh: ReplHelper) -> Optional[List[RRef]]
def repl_rref(rh: ReplHelper) -> Optional[RRef]
def repl_cancel(rh: Optional[ReplHelper] = None) -> None
Trivial builtin stages
def mknode(r: Registry, cfgdict: dict, artifacts: Dict[Name,bytes] = {}, name: str = 'mknode') -> DRef
def redefine(stage: Any, new_config: Callable[[dict],None] = lambda x:None, new_matcher: Optional[Matcher] = None, new_realizer: Optional[Realizer] = None) -> Any
Define a new Derivation based on the existing one, by updating it's config, optionally re-writing it's matcher, or it's realizer.
Arguments:
stage:Any
aStage
function, accepting arbitrary keyword argumentsnew_config:Callable[[dict],None]
A function to update thedref
's config. Default varsion makes no changes.new_matcher:Optional[Matcher]=None
Optional new matcher (defaults to the existing matcher)new_realizer:Optional[Realizer]=None
Optional new realizer (defaults to the existing realizer)
Return:
A callable Stage
, accepting pass-through arguments
Example:
def _new_config(old_config):
old_config['learning_rate'] = 1e-5
return mkconfig(old_config)
realize1(instantiate(redefine(myMLmodel, _new_config)))
FIXME: Updating configs is dangerous: it changes its dref and thus breaks
dependencies. Only top-level stages should use new_confid
currently.
def realized(stage: Any) -> Stage
Asserts that the stage doesn't requre running its realizer. Re-defines stage realizer with a dummy realizer triggering an assertion.
Example:
rref:RRef=realize1(instantiate(realized(my_long_running_stage, arg="bla")))
# ^^^ Fail if `my_long_running_stage` is not yet realized.
Builtin stages for fetching things from the Internet
logger = getLogger(__name__)
info = logger.info
error = logger.error
CURL = try_executable('curl',
'PYLIGHTNIX_CURL',
'`curl` executable ...
AUNPACK = try_executable('aunpack',
'PYLIGHTNIX_AUNPACK',
'`aunp ...
def fetchurl2(url: str, sha256: Optional[str] = None, sha1: Optional[str] = None, name: Optional[str] = None, filename: Optional[str] = None, force_download: bool = False, r: Optional[Registry] = None, kwargs) -> DRef
Download file given it's URL addess.
Downloading is done by calling curl
application. The path to the executable
may be altered by setting the PYLIGHTNIX_CURL
environment variable.
Agruments:
r:Registry
the dependency resolution Registry.url:str
URL to download from. Should point to a single file.sha256:str
SHA-256 hash sum of the file.name:Optional[str]
: Name of the Derivation. The stage will attempt to deduce the name if not specified.filename:Optional[str]=None
Name of the filename on disk after downloading. Stage will attempt to deduced it if not specified.force_download:bool=False
If False, resume the last download if possible.check_promises:bool=True
Passed tomkdrv
as-is.
Example:
def hello_src(r:Registry)->DRef:
hello_version = '2.10'
return fetchurl2(
r,
name='hello-src',
url=f'http://ftp.gnu.org/gnu/hello/hello-{hello_version}.tar.gz',
sha256='31e066137a962676e89f69d1b65382de95a7ef7d914b8cb956f41ea72e0f516b')
rref:RRef=realize1(instantiate(hello_src))
print(rref2path(rref))
def unpack(path: Optional[str] = None, refpath: Optional[RefPath] = None, name: Optional[str] = None, sha256: Optional[str] = None, sha1: Optional[str] = None, aunpack_args: List[str] = [], r: Optional[Registry] = None, kwargs) -> DRef
Builtin stages for fetching things from the Internet
logger = getLogger(__name__)
info = logger.info
error = logger.error
WGET = try_executable('wget',
'PYLIGHTNIX_WGET',
'Executable `wget` ...
AUNPACK = try_executable('aunpack',
'PYLIGHTNIX_AUNPACK',
'`aunp ...
def _unpack_inplace(o: str, fullpath: str, remove_file: bool)
def fetchurl(url: str, sha256: Optional[str] = None, sha1: Optional[str] = None, mode: str = 'unpack,remove', name: Optional[str] = None, filename: Optional[str] = None, force_download: bool = False, check_promises: bool = True, r: Optional[Registry] = None, kwargs) -> DRef
Download and unpack an URL addess.
Downloading is done by calling wget
application. Optional unpacking is
performed with the aunpack
script from atool
package. sha256
defines the
expected SHA-256 hashsum of the stored data. mode
allows to tweak the
stage's behavior: adding word 'unpack' instructs fetchurl to unpack the
package, adding 'remove' instructs it to remove the archive after unpacking.
If 'unpack' is not expected, then the promise named 'out_path' is created.
Agruments:
r:Registry
the dependency resolution Registry.url:str
URL to download from. Should point to a single file.sha256:str
SHA-256 hash sum of the file.model:str='unpack,remove'
Additional options. Format:[unpack[,remove]]
.name:Optional[str]
: Name of the Derivation. The stage will attempt to deduce the name if not specified.filename:Optional[str]=None
Name of the filename on disk after downloading. Stage will attempt to deduced it if not specified.force_download:bool=False
If False, resume the last download if possible.check_promises:bool=True
Passed tomkdrv
as-is.
Example:
def hello_src(r:Registry)->DRef:
hello_version = '2.10'
return fetchurl(
r,
name='hello-src',
url=f'http://ftp.gnu.org/gnu/hello/hello-{hello_version}.tar.gz',
sha256='31e066137a962676e89f69d1b65382de95a7ef7d914b8cb956f41ea72e0f516b')
rref:RRef=realize1(instantiate(hello_src))
print(rref2path(rref))
def fetchlocal(sha256: str, path: Optional[str] = None, envname: Optional[str] = None, mode: str = 'unpack,remove', name: Optional[str] = None, filename: Optional[str] = None, check_promises: bool = True, r: Optional[Registry] = None, kwargs) -> DRef
Copy local file into Pylightnix storage. This function is typically intended to register application-specific files which are distributed with a source repository.
See fetchurl
for arguments description.
If 'unpack' is not expected, then the promise named 'out_path' is created.
FIXME: Switch regular fetchurl
to curl
and call it with file://
URLs.
Simple functions imitating unix shell tools.
def lsdref_(r: DRef, S=None) -> Iterable[str]
def lsrref_(r: RRef, fn: List[str] = [], S=None) -> Iterable[str]
def lsrref(r: RRef, fn: List[str] = [], S=None) -> List[str]
def lsref(r: Union[RRef,DRef], S=None) -> List[str]
List the contents of r
. For DRefs, return
realization hashes. For RRefs, list artifact files.
def catrref_(r: RRef, fn: List[str], S=None) -> Iterable[str]
def catref(r: Union[RRef,RefPath,Path], fn: List[str] = [], S=None) -> List[str]
Return the contents of r's artifact line by line. fn
is a list of
folders, relative to rref's root.
def rmref(r: Union[RRef,DRef], S=None) -> None
Forcebly remove a reference from the storage. Removing DRefs also removes all their realizations.
Currently Pylightnix makes no attempts to synchronize an access to the storage. In scenarious involving parallelization, users are expected to take care of possible race conditions.
def shell(r: Union[RRef,DRef,Build,Path,str,None] = None, S=None) -> None
Open the Unix Shell in the directory associated with the argument passed.
Path to the shell executable is read from the SHELL
environment variable,
defaulting to /bin/sh
. If r
is None, open the shell in the root of the
Pylightnix storage.
The function is expected to be run in REPL Python shells like IPython.
def shellref(r: Union[RRef,DRef,None] = None, S=None) -> None
Alias for shell. Deprecated.
def du(S=None) -> Dict[DRef,Tuple[int,Dict[RRef,int]]]
Calculates the disk usage, in bytes. For every derivation, return it's total disk usage and disk usages per realizations. Note, that total disk usage of a derivation is slightly bigger than sum of it's realization's usages.
def find(name: Optional[Union[Stage,str]] = None, newer: Optional[float] = None, S: Optional[StorageSettings] = None) -> List[RRef]
Find RRefs in Pylightnix sotrage which match all of the criteria provided. Without arguments return all RRefs.
Arguments:
name:Optional[Union[Stage,str]]=None
match RRefs which havename
in their name. Matching is done byfnmatch
Python function which supports shell-like glob expressions with '*' and '?' symbols. If name is a Stage then it is instantiated and it's name is taken.newer:Optional[float]=None
match RRefs which are newer than this number of seconds starting from the UNIX Epoch. Zero and negative numbers count backward from the current time.
FIXME: If name is a stage, then this function instantiates this stage before searching. Thus, the storage is moified, which may be a undesired behaviour
def diff(stageA: Union[RRef,DRef,Stage], stageB: Union[RRef,DRef,Stage], S=None) -> None
Run system's diff
utility to print the difference between configs of 2
stages passed.
Note: if argument is a Stage, it is instantiated first
def linkrref(rref: RRef, destdir: Optional[Path] = None, format: str = '_rref_%(T)s_%(N)s', S=None) -> Path
linkkrref creates a symbolic link to a particular realization reference.
The new link appears in the destdir
directory if this argument is not None,
otherwise the current directory is used.
Format accepts the following Python pattern tags:
%(T)s
replaced with the build time%(N)s
replaced with the config name
Informally, linkrref
creates the link:
{tgtpath}/{format} --> $PYLIGHTNIX_STORE/{dref}/{rref}
.
The function overwrites existing symlinks.
def linkdref(dref: DRef, destdir: Optional[Path] = None, format: str = '_rref_%(N)s', S=None) -> Path
def linkrrefs(rrefs: Iterable[RRef], destdir: Optional[Path] = None, format: str = '_rref_%(T)s_%(N)s', S=None) -> List[Path]
A Wrapper around linkrref
for linking a set of RRefs.
Lens module defines the Lens
helper class, which offers quick navigation
through the dependent configurations
LensContext = NamedTuple('LensContext',[('S',Optional[StorageSettings]),
( ...
def val2dict(v: Any, ctx: LensContext) -> Optional[dict]
Return the dict
representation of the Lens value, if possible. Getting
the dictionary allows for creating new lenses
def val2rref(v: Any, ctx: LensContext) -> RRef
def val2path(v: Any, ctx: LensContext) -> Path
Resolve the current value of Lens into system path. Assert if it is not possible or if the result is associated with multiple paths.
FIXME: re-use val2rref here
def traverse(l: "Lens", hint: str) -> Any
def mutate(l: "Lens", v: Any, hint: str) -> None
def lens_repr(l, accessor: str) -> str
def __init__(self, ctx: LensContext, start: Any, steps: List[str]) -> None
A Lens is a "syntactic sugar" helper object which could traverse through various Python and Pylightnix tree-like structures in a uniform way.
The list of supported structures include:
Lens lifecycle typically consists of three stages:
- Lens creation with mklens helper function.
- Navigation through the nested fileds using regular Python dot-notation. Accessing Lens's attributes results in the creation of new Lens.
- Access to the raw value which could no longer be converted into a Lens. In
this case the raw value is returned. See
val
,optval
,rref
,dref
, etc.
Lenses are not inteded to be created directly, consider using mklens constructor.
def __init__(self, ctx: LensContext, start: Any, steps: List[str]) -> None
Arguments:
ctx
- is the context in which this Lens is created. The more information it contains the more functions are availablestart
- Source object which we exploresteps
- List of attributes to query
def __getattr__(self, key) -> "Lens"
Sugar for Lens.get
def get(self, key) -> "Lens"
Return a new Lens out of the key
attribute of the current Lens
@property
def optval(self) -> Optional[Any]
Return the value of Lens as-is
@val.setter
def val(self, v)
@property
def refpath(self) -> RefPath
Check that the current value of Lens is a RefPath
and return it
@property
def dref(self) -> DRef
Check that the current value of Lens is a DRef
and return it
@property
def syspath(self) -> Path
Check that the current value of Lens is a Path
and return it
@property
def contents(self) -> str
Check that the current value of Lens is a Path
and return it
@property
def rref(self) -> RRef
Check that the current value of Lens is an RRef
and return it
@property
def closure(self) -> Closure
Constructs a closure of the DRef which this lens points to. FIXME: Filter the closure derivations from unrelated entries.
def mklens(x: Any, o: Optional[Path] = None, b: Optional[Build] = None, rref: Optional[RRef] = None, ctx: Optional[Context] = None, closure: Optional[Closure] = None, build_output_idx: int = 0, S: Optional[StorageSettings] = None, r: Optional[Registry] = None) -> Lens
mklens creates Lens objects from various Pylightnix objects.
Arguments:
x:Any
The object to create the Lens from. Supported source object types are:RRefs
DRefs
Build
RefPath
dict
b:Optional[Build]=None
OptionalBuild
context of the Lens. Passing this object would allow Lens to resolve RRefs using the Context of the current realization. Also it would allow the Lens to use build_path function to resolve Build paths.rref:Optional[RRef]=None
OptionalRRef
link. Passing this object will allow Lens to resolve other RRefs using the Context of the given RRef.ctx:Optional[Context]=None
Passing optional Context would allow Lens to resolve RRefs.build_output_idx:int=0
ForBuilds
, specify the index of output path, defaulted to zero
Example:
stage=partial(fetchurl, url='http://example.com',
sha256='...',
output=[promise,'file.txt'],
foo={'bar':42}, # Additional configuration item
)
dref:DRef=instantiate(stage).dref
mklens(dref).url.val # Access raw value of 'url'
mklens(dref).foo # Return another lens pointing at 'foo'
mklens(dref).foo.val # Return raw value of 'foo' (a dict)
mklens(dref).foo.bar.val # Return raw value of 'bar'
mklens(dref).foo.refpath # Error! dict is not a path
mklens(dref).output.val # Return raw output value
mklens(dref).output.refpath # Return output as a RefPath (a list)
mklens(dref).output.syspath # Error! not a realization
rref:RRef=realize1(instantiate(stage))
mklens(rref).output.syspath # Return output as a system path
_REF = TypeVar('_REF')
_REF is either Path
or RRef
ExceptionText = str
Alias for exception text
def __init__(self, right: Optional[Output[_REF]] = None, left: Optional[Tuple[List[_REF],ExceptionText]] = None)
Either is a poor-man's (EitherT Exception Ouput)
monad. It contains
either (RIGHT) result of a realization or (LEFT) error report together with
half-done results to show to the user.
Either
should be considered as an "upgrade" for regular Output, which allows
user to record the fact of realization failure into the special kind of
realization result rather than rasing an exception.
TODO: Stress the attention on the fact that Either now encodes the list of items which may not have the same meaning. Some items may now be 'failed' and we may need a mapping function to apply matchers to them.
TODO: Think about whether we should mark the fact that a stage uses Either wrappert in the stage configuration or not. Probably we should, because either-realizers in general are not backward-compatible with regular realizers.
def __init__(self, right: Optional[Output[_REF]] = None, left: Optional[Tuple[List[_REF],ExceptionText]] = None)
def mkright(o: Output[_REF]) -> Either[_REF]
Create a RIGHT Either
def mkleft(paths: List[Path], exc: ExceptionText) -> Either[Path]
Create a LEFT Either
def either_paths(e: Either[_REF]) -> List[_REF]
def either_isRight(e: Either[_REF]) -> bool
def either_isLeft(e: Either[_REF]) -> bool
def either_status(e: Either[_REF]) -> Optional[ExceptionText]
def either_loadR(rrefs: List[RRef], S) -> Either[RRef]
def either_validate(dref: DRef, e: Either[Path], S=None) -> List[Path]
def either_realizer(f: Callable[[Optional[StorageSettings],DRef,
Context,RealizeArg],Output[Path]]) -> Callable[[Optional[StorageSettings],DRef,
Context,RealizeArg],List[Path]]
Implements poor-man's (EitherT Exception Ouput)
monad.
Either, stages become either LEFT (if rasied an error) or
RIGHT (after normal completion). If the stage turns LEFT, then so will be any
of it's dependant stages.
Stages which use either_wrapper
typically don't use claims
instead of
promises
to allow the existance of LEFT-versions of themselves.
Either-stages should use appropriate matchers which supports LEFT-mode.
def either_matcher(m: MatcherO) -> Matcher
Convert an Either-matcher into the regular Matcher
def mkdrvE(config: Config, matcher: MatcherO, realizer: RealizerO, m: Optional[Registry] = None) -> DRef
def realizeE(closure: Closure, force_rebuild: Union[List[DRef],bool] = [], assert_realized: List[DRef] = [], realize_args: Dict[DRef,RealizeArg] = {}) -> Either[RRef]
def realizeManyE(closure: Union[Closure,Tuple[Any,Closure]], force_rebuild: Union[List[DRef],bool] = [], assert_realized: List[DRef] = [], realize_args: Dict[DRef,RealizeArg] = {}) -> Either[RRef]
Functions for moving parts of the storage to and from archives.
APACK = try_executable('apack',
'PYLIGHTNIX_APACK',
'`apack` execu ...
AUNPACK = try_executable('aunpack',
'PYLIGHTNIX_AUNPACK',
'`aunpack` ...
def spack(roots: List[RRef], out: Path, S: Optional[StorageSettings] = None) -> None
def sunpack(archive: Path, S=None) -> None
def deref_(ctxr: RRef, dref: DRef, S)
query the context for specific dref. If not present - then it must be a context holder itself.
def copyclosure(rrefs_S: Iterable[RRef], S: StorageSettings, D: Optional[StorageSettings] = None) -> None
Copy the closure of rrefs
from source storage S
to the destination
storage D
. If D
is None, use the default global storage as a desitnation.
TODO: Implement a non-recursive version.
A class for proxy objects where realization config parameters are set as attributs.
def unroll(ctx: Context, dref: DRef, b: Optional[Build], rindex: int, max_depth: Optional[int] = None, always_multiref: bool = False, S=None) -> Attrs
def autodrv_(kwargs: dict, nouts: int = 1, matcher: Optional[Matcher] = None, always_multiref: bool = False, sourcedeps: List[Any] = [])
def autodrv(args, *,, ,, =, ,, kwargs)
def autostage_(nouts: int = 1, matcher: Optional[Matcher] = None, always_multiref: bool = False, sourcedeps: List[Any] = [], decokw)
def autostage(args, *,, ,, =, ,, kwargs)
Builds a Pylightnix Stage out of a Python function. The decorator's arguments form the Configuration of a stage. After that, they go through the certain transformations and finally appear as the inner function's arguments. The transformation rules are explained in the table below.
Argument name in decorator | Argument type in decorator | Argument name in function | Argument type in function | Comment |
---|---|---|---|---|
r=None |
Optional[Registry] | - | - | Registry to register this stage in |
sourcedeps=[] |
List[Any] |
- | - | List of arbitrary Python objects to track by source in addition to the source of the wrapped function |
matcher=match_latest |
Matcher | - | - | Matcher to set for this stage. |
always_multiref=False |
bool |
- | - | Set to True to represent dependencies as lists even if they include only one matched realization. |
x | [selfref,str,...] | x | str |
A promise to produce a file or folder |
x | DRef | x | Attrs or List[Attrs] or RRef or List[RRef] |
Attrs with attributs of parent realization(s) or raw Realization references |
x | t | x | t | JSON-compatible arguments (bool ,int ,float ,str ,lists and dicts of thereof) are passed without changes |
nouts=1 | int | rindex | int | Number of realizations to produce for this stage in one run (defaults to 1) |
- | - | build |
Build | Build context for the current stage |
Example:
with current_registry(Registry(S)) as r:
@autostage(a=42)
def stage1(a,build):
assert a==42
@autostage(b=33,ref_stage1=stage1())
def stage2(b,build,ref_stage1):
assert b==33
assert ref_stage1.a==42
r1=realize1(instantiate(stage2))
assert mklens(r1,S=S).b.val==33