Skip to content

Commit

Permalink
Add named constructors for Thing.
Browse files Browse the repository at this point in the history
PiperOrigin-RevId: 577250818
  • Loading branch information
isingoo authored and copybara-github committed Oct 27, 2023
1 parent 8e6366f commit b9b4917
Show file tree
Hide file tree
Showing 2 changed files with 73 additions and 22 deletions.
76 changes: 63 additions & 13 deletions nisaba/scripts/natural_translit/utils/type_op.py
Original file line number Diff line number Diff line change
Expand Up @@ -92,14 +92,53 @@ class Thing:
values point to the same phoneme in the inventory.
"""

def __init__(
self,
alias: str = '', text: str = '',
value: ... = UNSPECIFIED, allow_none: bool = False
):
self.alias = alias
self.text = text
self.value = value_of(value) if exists(value, allow_none) else self
def __init__(self):
c = class_of(self)
self.alias = '%s_%d' % (c, hash(self))
self.text = 'Undefined %s' % c
self.value = self

@classmethod
def named(cls, alias: str) -> 'Thing':
new = cls()
new.set_alias(alias)
new.text = class_and_alias(new)
return new

@classmethod
def from_value(cls, value: ...) -> 'Thing':
"""Makes a Thing from a value.
Args:
value: If the value is an object with 'value' attribute, the value is
set to value.value in order to avoid nesting values in dynamically
created things and making their equivalence invisible to is_equal().
If this is undesirable, use store_as().
Returns:
Thing
"""
new = cls()
value = value_of(value)
new.text = 'from:%s' % class_and_text(value)
new.value = value_of(value)
return new

@classmethod
def store_as(cls, alias: str, value: ...) -> 'Thing':
"""Makes a Thing with a custom alias and value."""
new = cls()
new.set_alias(alias)
new.text = 'store_%s:%s' % (new.alias, class_and_text(value))
new.value = value
return new

def set_alias(self, alias: str) -> None:
# TODO: ensure alias conforms to inventory field name restrictions.
if alias:
self.alias = alias
else:
debug_message('set_alias', 'empty alias is not allowed')

# Union types

Expand Down Expand Up @@ -141,8 +180,11 @@ def class_of(a: ...) -> str:
def text_of(a: ...) -> str:
"""Returns str() for objects with no text attribute."""
if hasattr(a, 'text'):
return ('Textless %s' % class_of(a)) if is_empty(a.text) else a.text
return a.string() if isinstance(a, pyn.Fst) else str(a)
text = a.text
elif isinstance(a, pyn.Fst):
text = a.string()
else: text = str(a)
return text if text else '<no_text>'


def texts_of(*args) -> str:
Expand All @@ -154,6 +196,14 @@ def alias_of(a: ...) -> str:
return a.alias if hasattr(a, 'alias') and not_empty(a.alias) else text_of(a)


def class_and_alias(a: ...) -> str:
return '%s_%s' % (class_of(a), alias_of(a))


def class_and_text(a: ...) -> str:
return '%s_%s' % (class_of(a), text_of(a))


def value_of(a: ...) -> ...:
"""If a has no value attribute, returns a."""
return a.value if isinstance(a, Thing) else a
Expand Down Expand Up @@ -270,7 +320,7 @@ def enforce_thing(t: ...) -> Thing:
debug_message(
'enforce_thing', 'Thing from %s: %s' % (class_of(t), text_of(t))
)
return Thing(text=text_of(t), value=t)
return Thing.from_value(t)

# Attribute functions with type check.

Expand Down Expand Up @@ -586,12 +636,12 @@ def enforce_set(
try:
result.add(element)
except TypeError:
result.add(Thing(value=element))
result.add(Thing.from_value(element))
else:
try:
result.add(s)
except TypeError:
result.add(Thing(value=s))
result.add(Thing.from_value(s))
return result


Expand Down
19 changes: 10 additions & 9 deletions nisaba/scripts/natural_translit/utils/type_op_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,12 @@

D = collections.namedtuple('D', ['k'])
_D1 = D('v')
_T0 = t.Thing(alias='T0', text='t0', value=0)
_T1 = t.Thing(alias='T1', text='t1', value=t.UNSPECIFIED)
_T2 = t.Thing(alias='T2', text='', value=_T0)
_T3 = t.Thing(alias='', text='t3', value=_T1)
_T4 = t.Thing(alias='T4', text='t4', value=_T1)
_T0 = t.Thing.store_as(alias='T0', value=0)
_T1 = t.Thing.named(alias='T1')
_T2 = t.Thing.from_value(value=_T0)
_T3 = t.Thing.store_as(alias='', value=_T1)
_T4 = t.Thing.store_as(alias='T4', value=_T1)
_T5 = t.Thing.store_as(alias='T5', value=t.pyn.accep(''))


class TypeOpTest(absltest.TestCase):
Expand Down Expand Up @@ -140,10 +141,10 @@ def test_get_attribute_type(self):
)

def test_text_of_thing(self):
self.assertEqual(t.text_of(_T1), 't1')
self.assertEqual(t.text_of(_T1), 'Thing_T1')

def test_text_of_thing_empty(self):
self.assertEqual(t.text_of(_T2), 'Textless Thing')
self.assertEqual(t.text_of(_T5), 'store_T5:Fst_<no_text>')

def test_text_of_str(self):
self.assertEqual(t.text_of('abc'), 'abc')
Expand All @@ -167,7 +168,7 @@ def test_alias_of_thing(self):
self.assertEqual(t.alias_of(_T1), 'T1')

def test_alias_of_thing_empty(self):
self.assertEqual(t.alias_of(_T3), 't3')
self.assertNotEmpty(t.alias_of(_T3))

def test_alias_of_list(self):
self.assertEqual(t.alias_of([0, 1]), '[0, 1]')
Expand Down Expand Up @@ -387,7 +388,7 @@ def test_enforce_dict_namedtuple(self):

def test_enforce_dict_thing(self):
self.assertEqual(
t.enforce_dict(_T1), {'alias': 'T1', 'text': 't1', 'value': _T1}
t.enforce_dict(_T1), {'alias': 'T1', 'text': 'Thing_T1', 'value': _T1}
)

def test_enforce_dict_no_key(self):
Expand Down

0 comments on commit b9b4917

Please sign in to comment.