Skip to content

Commit

Permalink
WORK IN PROGRESS
Browse files Browse the repository at this point in the history
  • Loading branch information
ndenny committed Jan 17, 2025
1 parent e3c8d5a commit 3eb5644
Show file tree
Hide file tree
Showing 9 changed files with 128 additions and 48 deletions.
62 changes: 39 additions & 23 deletions lib/Bastion/CARP.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@

from collections.abc import Sequence

from Bastion.Common import toJDN
from Bastion.Common import toJDN, isBoxedObject, isTagged


class ReplyStatus(tuple):
DEFAULT_CATEGORY_GLOSS = {
Expand Down Expand Up @@ -258,12 +259,17 @@ def inconclusive(self):
class isReport(isResult):
def __init__(self, request, status, record = None, *args, **kwargs):
isResult.__init__(self, request, status, record, *args, **kwargs)
self._body = kwargs.get('report', None)
#-- Can directy specify the human readable expression with "report ="
self._body = kwargs.get('report', None)
#-- Can delegate construction of human readable expression with "humanize = "
self._humanize = kwargs.get('humanize', None)

@property
def body(self):
if getattr(self, '_body', None) is None:
if callable(getattr(self.record, 'toJDN', None)):
if callable(self._humanize):
self._body = self._humanize(self)
elif callable(getattr(self.record, 'toJDN', None)):
self._body = pprint.pformat(self.record.toJDN())
else:
self._body = pprint.pformat(self.record)
Expand Down Expand Up @@ -330,41 +336,51 @@ class isReceipt:
Mostly, I'm used for run-time type checking.
"""
def toJDN(self, **kwargs):
raise NotImplementedError("is subclass responsibility")
raise NotImplementedError(".toJDN is subclass responsibility")


class isReceiptOfResults(isReceipt):
class isWorkflowReceipt(isReceipt, isTagged):
"""
I am a thin wrapper around a sequence of results.
I am mostly used to build a single "record" from a sequence of operations,
with each operation having its own report.
Results can also be given with a key as part of a tuple such as (stage, result) where stage is a string key.
I am mostly used to build a single "record" from a sequence of actions with each action having its own result.
Results can also be given with an optional key as part of a tuple such as (stage, result) where stage is a string key.
isWorkflowReceipt(result1, result2, ...)
isWorkflowReceipt([('planning-stage', result1), ('running-stage', result2), ...])
"""
def __init__(self, results):
def __init__(self, *args):
self.results = [ ]
self.tags = { }
for result in results:
if isinstance(result, isResult):
self.results.append(result)
elif isinstance(result, Sequence) and (len(result) == 2) and isinstance(result[1], isResult):
tag = str(result[0])
result_actual = result[1]
self.results.append(result_actual)
self.tags[tag] = len(self.results)
else:
raise ValueError("result must be an instance of Bastion.CARP.isResult")
self._tags = { }
for result in args:
self.append(result)

def append(self, result, *args):
if isinstance(result, isResult):
self.results.append(result)
i = len(self.results)
for arg in args:
self._tags[str(arg)] = i
elif isinstance(result, isBoxedObject):
self.results.append(result.actual)
i = len(self.results)
for tag in result.tags:
self._tags[tag] = i
else:
raise ValueError("result must be an instance of Bastion.CARP.isResult")

@property
def tags(self):
return frozenset(self._tags.keys())

def __getitem__(self, n):
if isinstance(n, int):
return self.results[n]
if isinstance(n, str):
i = self.keyed[n]
i = self._tags[n]
return self.results[i]
raise KeyError(n)

def __iter__(self):
staged = [self.results[i] for i in range(len(self.results))]
return iter(staged)
return iter(self.results)

def toJDN(self, **kwargs):
jdn = { }
Expand Down
1 change: 1 addition & 0 deletions lib/Bastion/Chronology.py
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,7 @@ def earliest(self):
elapsed_seconds = (self.qM * Quantim.QUANTIM) * SECONDS
return (y + elapsed_seconds)

@property
def latest(self):
"""
answers a python datetime.datetime object that is the latest time possible within the range of this quantum.
Expand Down
5 changes: 2 additions & 3 deletions lib/Bastion/Clerks/BFD.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,12 @@

class Clerk(isClerk):
def __init__(self, vault, root = None, **kwargs):
isClerk.__init__(self)
isClerk.__init__(self, vault)

assert isinstance(vault, isVault), "vault must be an instance of Bastion.Model.isVault"
if root:
assert isinstance(root, pathlib.PosixPath), "root must be an instance of PosixPath"

self.vault = vault
self.root = root if root is not None else vault.bank

#-----------------------------------------
Expand Down Expand Up @@ -76,7 +75,7 @@ def manifest(self, *args):
else:
raise ValueError

cell = self.bank / ark.site / ark.zone / ark.asset
cell = self.root / ark.site / ark.zone / ark.asset
if cell.exists():
blondes = [ ]
for item in cell.iterdir():
Expand Down
71 changes: 63 additions & 8 deletions lib/Bastion/Common.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
import logging
import urllib

from typing import Any, FrozenSet

import yaml


Expand Down Expand Up @@ -114,33 +116,86 @@ def _toJDN_path(x, **kwargs):
toJDN.magic.append( (Exception, _toJDN_Exception) )
toJDN.magic.append( (pathlib.PurePath, _toJDN_path) )

class isTagged:
@property
def tags(self):
raise NotImplementedError(".tags -> FrozenSet[str] is subclass responsibility")

@property
def hasTags(self):
return (len(self.tags) > 0)


class entity:
class isBoxedObject(tuple, isTagged):
"""
I am syntactic sugar.
Mostly, I do type checking, e.g.
x = 4
if entity(x).isNumeric: print("it's a number!")
"""
def __init__(self, subject):
self.subject = subject
def __new__(cls, obj, *args):
return tuple.__new__(cls, [obj] + [str(arg) for arg in args])

value = property(operator.itemgetter(0))
actual = value #-- alias for "value" accessor

def __repr__(self):
if not self.hasTags:
return "|{}|".format(repr(self.actual))
else:
hashtags = ['#{}'.format(tag) for tag in sorted(self.tags)]
label = ",".join(hashtags)
return "|{}|~[{}]".format(repr(self.actual), label)

@property
def tags(self):
if getattr(self, '_tags', None) is None:
self._tags = frozenset(self[1:])
return self._tags

@property
def isNumeric(self):
return isinstance(self.subject, pynumbers.Real)
return isinstance(self.value, pynumbers.Real)

@property
def isDuration(self):
return isinstance(self.subject, datetime.timedelta)
return isinstance(self.value, datetime.timedelta)

@property
def isString(self):
return isinstance(self.subject, str)
return isinstance(self.value, str)

@property
def hasRDN(self):
return hasattr(self.subject, 'RDN')
return hasattr(self.value, 'RDN')

@property
def hasTags(self):
return (len(self) > 1)

def __call__(self):
return self.value

def tagged(self, *args):
return self.__class__(self.value, *args)


def entity(obj):
"""
Shorthand for creating BoxedObject instances.
e.g. entity(4).isNumeric
e.g. entity(4, "low", "number")
e.g. entity(4).tagged("low", "number")
"""
return isBoxedObject(obj)


def tag(obj, *args):
"""
Shorthand for creating BoxedObject instances.
This particular function signals that the primary purpose of the box is to associate tags.
"""
return isBoxedObject(obj, *args)


class UnknownType(object):
Expand Down Expand Up @@ -386,7 +441,7 @@ def prefer(options, **kwargs):
return choices[0]

return choices[:n]



def asLogLevel(x):
Expand Down
5 changes: 4 additions & 1 deletion lib/Bastion/Curator.py
Original file line number Diff line number Diff line change
Expand Up @@ -204,7 +204,7 @@ def anchor(self, item):
.anchor(item:Snap)
"""
if isinstance(item, BLONDE):
return self._anchors[item.anchor]
return self._anchors[item.genus]
if isinstance(item, str):
return self.anchor( BLONDE.decode(item) )
if isinstance(item, Snap):
Expand Down Expand Up @@ -237,6 +237,9 @@ def __init__(self, head, anchor):
self.head = head
self.anchor = anchor

def __repr__(self):
return "({}~{})".format(str(self.head), str(self.anchor))

@property
def genus(self):
return self.anchor.genus
Expand Down
3 changes: 1 addition & 2 deletions lib/Bastion/Movers/BFD.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,7 @@

class Mover(isMover):
def __init__(self, vault, **kwargs):
isMover.__init__(self)
self.vault = vault
isMover.__init__(self, vault)
self.bank = kwargs.get('bank', vault.bank)

assert isinstance(self.vault, isVault), "vault must be an instance of Bastion.Model.isVault"
Expand Down
6 changes: 3 additions & 3 deletions lib/Bastion/Packers/TARs.py
Original file line number Diff line number Diff line change
Expand Up @@ -126,11 +126,11 @@ def pack(self, subject, *args, **kwargs):
#-- use the built-in python tar archiver, using "pax" (POSIX.1-2001) extensions.
pax(tarp, request.asset.halo, **kwargs)
except Exception as err:
report = Report.Failed(request, err)
report = request.failed(err)
else:
#-- create an answer frame that will be part of the report.
packed = PackingReceipt(request.asset, tag, catalog(tarp), tarp)
report = Report.Success(request, packed)
report = request.succeeded(packed)

#-- answer the BLONDE of the newly created package.
return report
Expand All @@ -142,7 +142,7 @@ def unpack(self, subject, *args, **kwargs):
.unpack(request)
.unpack(halo, root)
"""
raise NotImplementedError
raise NotImplementedError(".unpack is subclass responsibility")


def catalog(tarp):
Expand Down
7 changes: 4 additions & 3 deletions lib/Bastion/Vaults/CARP.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,17 @@
import logging
import datetime

from Bastion.CARP import isRequest, isReceiptOfResults, isReport
from Bastion.CARP import isRequest, isWorkflowReceipt, Report
from Bastion.Curator import BLONDE

logger = logging.getLogger(__name__)


class PushReceipt(isReceiptOfResults):
class PushReceipt(isWorkflowReceipt):
pass

class PushReport(isReport):

class PushReport(Report):
#----------------------------------------------------------------------
#-- BEGIN ACCESSORS |
#-- These properties are pass throughs to the initial request object. |
Expand Down
16 changes: 11 additions & 5 deletions lib/Bastion/Vaults/Common.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,23 +33,29 @@ def push(self, asset, basis = None, **kwargs):
{asset} - an instance of Bastion.Model.isAsset
{basis} - can be a datetime or a BLONDE.
"""
#-- Build the request object.
#-- Build the request object and an empty (anticipating!) receipt.
request = PushRequest(asset, basis, **kwargs)
receipt = PushReceipt()


#-- Do the pack activity.
packing = self.pack(asset, basis, **kwargs)
receipt.append(packing, 'packed')

if packing.failed:
return request.failed( PushReceipt([('packed',packing)]) )
return request.failed(receipt)

movement = self.put(packing.record.tarp, packing.record.tag)
movement = self.put(packing.record.tarp, packing.record.tag)
receipt.append(movement, 'moved')

#-- clean up!
if packing.record.tarp.exists():
packing.record.tarp.unlink()

if movement.failed:
return request.failed( PushReceipt([('packed', packing), ('moved', movement)]) )
return request.failed( PushReceipt(tag(packing, 'packed'), tag(movement, 'moved')) )
else:
return request.succeeded( PushReceipt([('packed', packing), ('moved', movement)]) )
return request.succeeded( PushReceipt(tag(packing, 'packed'), tag(movement, 'moved')) )

def pull(self, ark, **kwargs):
raise NotImplementedError

0 comments on commit 3eb5644

Please sign in to comment.