Skip to content

Commit

Permalink
Initial package updates and test updates
Browse files Browse the repository at this point in the history
  • Loading branch information
BrianSipos committed Jan 31, 2025
1 parent 7cccbc9 commit 93f9a37
Show file tree
Hide file tree
Showing 18 changed files with 520 additions and 397 deletions.
2 changes: 1 addition & 1 deletion src/ace/adm_set.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ def get_modules_and_revisions(self, _ctx):
result = []
for adm_mod in found.all():
rev = adm_mod.revisions[0].name if adm_mod.revisions else None
result.append((adm_mod.name, rev, ('yang', adm_mod.source_id)))
result.append((adm_mod.module_name, rev, ('yang', adm_mod.source_id)))

for file_entry in self._file_entries:
name, ext = os.path.splitext(file_entry.name)
Expand Down
48 changes: 32 additions & 16 deletions src/ace/adm_yang.py
Original file line number Diff line number Diff line change
Expand Up @@ -128,19 +128,19 @@ class AriTextDecoder:

def __init__(self):
self._ari_dec = ari_text.Decoder()
self._adm_id = None
self._ns_id = None

def set_adm(self, adm_id: Union[str, int]):
def set_namespace(self, org_id: Union[str, int], model_id: Union[str, int]):
''' Set the ID of the current ADM for resolving relative ARIs.
'''
self._adm_id = adm_id
self._ns_id = (org_id, model_id)

def decode(self, text:str) -> ARI:
''' Decode ARI text and resolve any relative reference.
'''
ari = self._ari_dec.decode(io.StringIO(text))
if self._adm_id is not None:
ari = ari.map(RelativeResolver(self._adm_id))
if self._ns_id is not None:
ari = ari.map(RelativeResolver(*self._ns_id))
return ari


Expand Down Expand Up @@ -394,7 +394,7 @@ def _check_ari(self, ari:ARI):
''' Verify ARI references only imported modules. '''
if isinstance(ari, ReferenceARI):
imports = [mod[0] for mod in self._module.i_prefixes.values()]
if ari.ident.ns_id is not None and ari.ident.ns_id not in imports:
if ari.ident.module_name is not None and ari.ident.module_name not in imports:
raise ValueError(f'ARI references module {ari.ident.ns_id} that is not imported')

def _get_ari(self, text:str) -> ARI:
Expand Down Expand Up @@ -617,15 +617,28 @@ def decode(self, buf: TextIO) -> AdmModule:

adm = AdmModule()
adm.source = src
adm.name = module.arg
adm.module_name = module.arg
# Normalize the intrinsic ADM name
adm.norm_name = normalize_ident(adm.name)
adm.norm_name = normalize_ident(adm.module_name)
self._adm = adm
self._ari_dec.set_adm(self._adm.norm_name)

ns_stmt = module.search_one('namespace')
if ns_stmt is None:
raise RuntimeError('ADM module is missing "namespace" statement')
ns_ari = self._ari_dec.decode(ns_stmt.arg)
self._ari_dec.set_namespace(ns_ari.ident.org_id, ns_ari.ident.model_id)
adm.ns_org_name = ns_ari.ident.org_id.casefold()
adm.ns_model_name = ns_ari.ident.model_id.casefold()

org_stmt = module.search_one('organization')
if org_stmt:
enum_stmt = org_stmt.search_one((AMM_MOD, 'enum'))
if enum_stmt:
adm.ns_org_enum = int(enum_stmt.arg)

enum_stmt = module.search_one((AMM_MOD, 'enum'))
if enum_stmt:
adm.enum = int(enum_stmt.arg)
adm.ns_model_enum = int(enum_stmt.arg)

for sub_stmt in module.search('import'):
prefix_stmt = sub_stmt.search_one('prefix')
Expand Down Expand Up @@ -664,7 +677,7 @@ def decode(self, buf: TextIO) -> AdmModule:
self._get_section(adm.oper, Oper, module)
self._get_section(adm.var, Var, module)
except Exception as err:
raise RuntimeError(f'Failure processing object definitions from ADM "{adm.name}": {err}') from err
raise RuntimeError(f'Failure processing object definitions from ADM "{adm.module_name}": {err}') from err

self._module = None
return adm
Expand Down Expand Up @@ -697,15 +710,17 @@ def encode(self, adm: AdmModule, buf: TextIO) -> None:
:param adm: The ORM root object.
:param buf: The buffer to write into.
'''
module = pyang.statements.new_statement(None, None, None, 'module', adm.name)
module = pyang.statements.new_statement(None, None, None, 'module', adm.module_name)
self._module = module
self._denorm_prefixes = {}

self._add_substmt(module, 'yang-version', '1.1')
self._add_substmt(module, 'namespace', f'ari://{adm.name}/')
self._add_substmt(module, 'namespace', f'ari://{adm.ns_org_name}/{adm.ns_model_name}/')

for item in adm.metadata_list.items:
self._add_substmt(module, item.name, item.arg)
item_stmt = self._add_substmt(module, item.name, item.arg)
if item.name == 'organization' and adm.ns_org_enum is not None:
self._add_substmt(item_stmt, (AMM_MOD, 'enum'), str(adm.ns_org_enum))

for imp in adm.imports:
imp_stmt = self._add_substmt(module, 'import', imp.name)
Expand All @@ -719,8 +734,9 @@ def encode(self, adm: AdmModule, buf: TextIO) -> None:
modname = modtup[0]
self._denorm_prefixes[modname] = prefix

# prefixed keyword after v_init_module
self._add_substmt(module, (AMM_MOD, 'enum'), str(adm.enum))
if adm.ns_model_enum is not None:
# prefixed keyword after v_init_module
self._add_substmt(module, (AMM_MOD, 'enum'), str(adm.ns_model_enum))

for rev in adm.revisions:
sub_stmt = self._add_substmt(module, 'revision', rev.name)
Expand Down
43 changes: 31 additions & 12 deletions src/ace/ari.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
import datetime
from dataclasses import dataclass
import enum
from typing import Callable, Dict, List, Optional, Union
from typing import Callable, Dict, List, Optional, Tuple, Union
import cbor2
import numpy

Expand Down Expand Up @@ -280,25 +280,44 @@ class Identity:
''' The identity of an object reference as a unique identifer-set.
'''

ns_id:Union[str, int, None] = None
''' The None value indicates a module-relative path. '''
ns_rev:Optional[str] = None
''' For the text-form ARI a specific module revision date. '''
org_id:Union[str, int, None] = None
''' The None value indicates an org-relative path. '''
model_id:Union[str, int, None] = None
''' The None value indicates an model-relative path. '''
model_rev:Optional[str] = None
''' For the text-form ARI a specific ADM revision date. '''
type_id:Optional[StructType] = None
''' ADM type of the referenced object '''
obj_id:Union[str, int, None] = None
''' Name with the type removed '''

def __str__(self):
''' Pretty format the identity.
@property
def ns_id(self) -> Tuple:
''' Get a tuple representing the namespace. '''
return (self.org_id, self.model_id)

@property
def module_name(self) -> Optional[str]:
''' Get the ADM module name associated with this namespace. '''
if self.org_id is None or self.model_id is None:
return None
return f'{self.org_id}-{self.model_id}'

def __str__(self) -> str:
''' Pretty format the identity similar to URI text encoding.
'''
text = ''
if self.ns_id is None:
text += '.'
if self.org_id is None:
if self.model_id is None:
text += '.'
else:
text += '..'
else:
text += f'/{self.ns_id}'
if self.ns_rev:
text += f'@{self.ns_rev}'
text += f'/{self.org_id}'
if self.model_id is not None:
text += f'/{self.model_id}'
if self.ns_rev:
text += f'@{self.ns_rev}'
text += f'/{self.type_id.name}'
text += f'/{self.obj_id}'
return text
Expand Down
16 changes: 9 additions & 7 deletions src/ace/ari_cbor.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,20 +68,21 @@ def _item_to_ari(self, item:object):
LOGGER.debug('Got ARI item: %s', item)

if isinstance(item, list):
if len(item) >= 3:
if len(item) in {4, 5}:
# Object reference
type_id = StructType(item[1]) if item[1] is not None else None
type_id = StructType(item[2]) if item[2] is not None else None
ident = Identity(
ns_id=item[0],
org_id=item[0],
model_id=item[1],
type_id=type_id,
obj_id=item[2],
obj_id=item[3],
)

params = None
if len(item) >= 4:
if len(item) == 5:
params = [
self._item_to_ari(param_item)
for param_item in item[3]
for param_item in item[4]
]

res = ReferenceARI(ident=ident, params=params)
Expand Down Expand Up @@ -183,7 +184,8 @@ def _ari_to_item(self, obj:ARI) -> object:
if isinstance(obj, ReferenceARI):
type_id = int(obj.ident.type_id) if obj.ident.type_id is not None else None
item = [
obj.ident.ns_id,
obj.ident.org_id,
obj.ident.model_id,
type_id,
obj.ident.obj_id,
]
Expand Down
16 changes: 11 additions & 5 deletions src/ace/ari_text/encode.py
Original file line number Diff line number Diff line change
Expand Up @@ -223,14 +223,20 @@ def _encode_obj(self, buf: TextIO, obj:ARI, prefix:bool=False):
elif isinstance(obj, ReferenceARI):
if prefix:
buf.write('ari:')
if obj.ident.ns_id is None:
buf.write('.')
if obj.ident.org_id is None:
if obj.ident.model_id is None:
buf.write('.')
else:
buf.write('..')
else:
buf.write('//')
buf.write(str(obj.ident.ns_id))
if obj.ident.ns_rev is not None:
buf.write(str(obj.ident.org_id))
if obj.ident.model_id is not None:
buf.write('/')
buf.write(str(obj.ident.model_id))
if obj.ident.model_rev is not None:
buf.write('@')
buf.write(obj.ident.ns_rev)
buf.write(obj.ident.model_rev)
buf.write('/')

if obj.ident.type_id is not None and obj.ident.obj_id is not None:
Expand Down
52 changes: 34 additions & 18 deletions src/ace/ari_text/parsemod.py
Original file line number Diff line number Diff line change
Expand Up @@ -209,57 +209,73 @@ def p_params_amlist(p):


def p_objpath_only_ns(p):
'''objpath : SLASH SLASH VALSEG
| SLASH SLASH VALSEG SLASH'''
mod = util.MODID(p[3])
'''objpath : SLASH SLASH VALSEG SLASH VALSEG
| SLASH SLASH VALSEG SLASH VALSEG SLASH'''
org = util.IDSEGMENT(p[3])
mod = util.MODID(p[5])
if not isinstance(mod, tuple):
mod = (mod, None)

p[0] = Identity(
ns_id=mod[0],
ns_rev=mod[1],
org_id=org,
model_id=mod[0],
model_rev=mod[1],
type_id=None,
obj_id=None,
)


def p_objpath_with_ns(p):
'objpath : SLASH SLASH VALSEG SLASH VALSEG SLASH VALSEG'
'objpath : SLASH SLASH VALSEG SLASH VALSEG SLASH VALSEG SLASH VALSEG'
org = util.IDSEGMENT(p[3])
mod = util.MODID(p[5])
if not isinstance(mod, tuple):
mod = (mod, None)

try:
typ = util.get_structtype(p[5])
typ = util.get_structtype(p[7])
except Exception as err:
LOGGER.error('Object type invalid: %s', err)
raise RuntimeError(err) from err

# Reference are only allowed with AMM types
if typ >= 0 or typ == StructType.OBJECT:
raise RuntimeError("Invalid AMM type")

mod = util.MODID(p[3])
if not isinstance(mod, tuple):
mod = (mod, None)
obj = util.IDSEGMENT(p[9])

p[0] = Identity(
ns_id=mod[0],
ns_rev=mod[1],
org_id=org,
model_id=mod[0],
model_rev=mod[1],
type_id=typ,
obj_id=util.IDSEGMENT(p[7]),
obj_id=obj,
)


def p_objpath_relative(p):
'objpath : DOT SLASH VALSEG SLASH VALSEG'
'''objpath : DOT SLASH VALSEG SLASH VALSEG
| DOT DOT SLASH VALSEG SLASH VALSEG SLASH VALSEG'''
got = len(p)

if got > 6:
mod = util.MODID(p[got - 5])
if not isinstance(mod, tuple):
mod = (mod, None)
else:
mod = (None, None)

try:
typ = util.get_structtype(p[3])
typ = util.get_structtype(p[got - 3])
except Exception as err:
LOGGER.error('Object type invalid: %s', err)
raise RuntimeError(err) from err

# Reference are only allowed with AMM types
if typ >= 0 or typ == StructType.OBJECT:
raise RuntimeError("Invalid AMM type")

p[0] = Identity(ns_id=None, type_id=typ, obj_id=util.IDSEGMENT(p[5]))
obj = util.IDSEGMENT(p[got - 1])

p[0] = Identity(org_id=None, model_id=mod[0], model_rev=mod[1], type_id=typ, obj_id=obj)


def p_acbracket(p):
Expand Down
Loading

0 comments on commit 93f9a37

Please sign in to comment.