mirror of
https://github.com/RYDE-WORK/ballistica.git
synced 2026-02-03 05:53:15 +08:00
latest cloudtool and entity work
This commit is contained in:
parent
35097d28e6
commit
6adce18a4b
1
.idea/dictionaries/ericf.xml
generated
1
.idea/dictionaries/ericf.xml
generated
@ -1605,6 +1605,7 @@
|
|||||||
<w>testcapi</w>
|
<w>testcapi</w>
|
||||||
<w>testcapimodule</w>
|
<w>testcapimodule</w>
|
||||||
<w>testclass</w>
|
<w>testclass</w>
|
||||||
|
<w>testd</w>
|
||||||
<w>testfoo</w>
|
<w>testfoo</w>
|
||||||
<w>testfooooo</w>
|
<w>testfooooo</w>
|
||||||
<w>testhelpers</w>
|
<w>testhelpers</w>
|
||||||
|
|||||||
2
Makefile
2
Makefile
@ -330,7 +330,7 @@ pycharmfull: prereqs
|
|||||||
# Note: need to disable bytecode writing so we don't cause errors due to
|
# Note: need to disable bytecode writing so we don't cause errors due to
|
||||||
# unexpected __pycache__ dirs popping up.
|
# unexpected __pycache__ dirs popping up.
|
||||||
test: prereqs
|
test: prereqs
|
||||||
@tools/snippets pytest tests
|
@tools/snippets pytest -v tests
|
||||||
|
|
||||||
|
|
||||||
################################################################################
|
################################################################################
|
||||||
|
|||||||
@ -121,11 +121,11 @@ class EntityMixin:
|
|||||||
self.d_data = target.d_data
|
self.d_data = target.d_data
|
||||||
target.d_data = None
|
target.d_data = None
|
||||||
|
|
||||||
def get_pruned_data(self) -> Dict[str, Any]:
|
def pruned_data(self) -> Dict[str, Any]:
|
||||||
"""Return a pruned version of this instance's data.
|
"""Return a pruned version of this instance's data.
|
||||||
|
|
||||||
This varies from d_data in that values may be stripped out if
|
This varies from d_data in that values may be stripped out if
|
||||||
they are equal to defaults (if the field allows such).
|
they are equal to defaults (for fields with that option enabled).
|
||||||
"""
|
"""
|
||||||
import copy
|
import copy
|
||||||
data = copy.deepcopy(self.d_data)
|
data = copy.deepcopy(self.d_data)
|
||||||
@ -133,24 +133,26 @@ class EntityMixin:
|
|||||||
self.prune_fields_data(data)
|
self.prune_fields_data(data)
|
||||||
return data
|
return data
|
||||||
|
|
||||||
def to_json_str(self, pretty: bool = False) -> str:
|
def to_json_str(self, prune: bool = True, pretty: bool = False) -> str:
|
||||||
"""Convert the entity to a json string.
|
"""Convert the entity to a json string.
|
||||||
|
|
||||||
This uses bafoundation.jsontools.ExtendedJSONEncoder/Decoder
|
This uses bafoundation.jsontools.ExtendedJSONEncoder/Decoder
|
||||||
to support data types not natively storable in json.
|
to support data types not natively storable in json.
|
||||||
"""
|
"""
|
||||||
|
if prune:
|
||||||
|
data = self.pruned_data()
|
||||||
|
else:
|
||||||
|
data = self.d_data
|
||||||
if pretty:
|
if pretty:
|
||||||
return json.dumps(self.d_data,
|
return json.dumps(data,
|
||||||
indent=2,
|
indent=2,
|
||||||
sort_keys=True,
|
sort_keys=True,
|
||||||
cls=ExtendedJSONEncoder)
|
cls=ExtendedJSONEncoder)
|
||||||
return json.dumps(self.d_data,
|
return json.dumps(data, separators=(',', ':'), cls=ExtendedJSONEncoder)
|
||||||
separators=(',', ':'),
|
|
||||||
cls=ExtendedJSONEncoder)
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def json_loads(s: str) -> Any:
|
def json_loads(s: str) -> Any:
|
||||||
"""Load a json string with our special extended decoder.
|
"""Load a json string using our special extended decoder.
|
||||||
|
|
||||||
Note that this simply returns loaded json data; no
|
Note that this simply returns loaded json data; no
|
||||||
Entities are involved.
|
Entities are involved.
|
||||||
|
|||||||
@ -47,7 +47,7 @@ class Field(BaseField, Generic[T]):
|
|||||||
def __init__(self,
|
def __init__(self,
|
||||||
d_key: str,
|
d_key: str,
|
||||||
value: 'TypedValue[T]',
|
value: 'TypedValue[T]',
|
||||||
store_default: bool = False) -> None:
|
store_default: bool = True) -> None:
|
||||||
super().__init__(d_key)
|
super().__init__(d_key)
|
||||||
self.d_value = value
|
self.d_value = value
|
||||||
self._store_default = store_default
|
self._store_default = store_default
|
||||||
@ -86,7 +86,7 @@ class CompoundField(BaseField, Generic[TC]):
|
|||||||
def __init__(self,
|
def __init__(self,
|
||||||
d_key: str,
|
d_key: str,
|
||||||
value: TC,
|
value: TC,
|
||||||
store_default: bool = False) -> None:
|
store_default: bool = True) -> None:
|
||||||
super().__init__(d_key)
|
super().__init__(d_key)
|
||||||
if __debug__ is True:
|
if __debug__ is True:
|
||||||
from bafoundation.entity._value import CompoundValue
|
from bafoundation.entity._value import CompoundValue
|
||||||
@ -165,7 +165,7 @@ class ListField(BaseField, Generic[T]):
|
|||||||
def __init__(self,
|
def __init__(self,
|
||||||
d_key: str,
|
d_key: str,
|
||||||
value: 'TypedValue[T]',
|
value: 'TypedValue[T]',
|
||||||
store_default: bool = False) -> None:
|
store_default: bool = True) -> None:
|
||||||
super().__init__(d_key)
|
super().__init__(d_key)
|
||||||
self.d_value = value
|
self.d_value = value
|
||||||
self._store_default = store_default
|
self._store_default = store_default
|
||||||
@ -218,7 +218,7 @@ class DictField(BaseField, Generic[TK, T]):
|
|||||||
d_key: str,
|
d_key: str,
|
||||||
keytype: Type[TK],
|
keytype: Type[TK],
|
||||||
field: 'TypedValue[T]',
|
field: 'TypedValue[T]',
|
||||||
store_default: bool = False) -> None:
|
store_default: bool = True) -> None:
|
||||||
super().__init__(d_key)
|
super().__init__(d_key)
|
||||||
self.d_value = field
|
self.d_value = field
|
||||||
self._store_default = store_default
|
self._store_default = store_default
|
||||||
@ -280,7 +280,7 @@ class CompoundListField(BaseField, Generic[TC]):
|
|||||||
def __init__(self,
|
def __init__(self,
|
||||||
d_key: str,
|
d_key: str,
|
||||||
valuetype: TC,
|
valuetype: TC,
|
||||||
store_default: bool = False) -> None:
|
store_default: bool = True) -> None:
|
||||||
super().__init__(d_key)
|
super().__init__(d_key)
|
||||||
self.d_value = valuetype
|
self.d_value = valuetype
|
||||||
|
|
||||||
@ -369,7 +369,7 @@ class CompoundDictField(BaseField, Generic[TK, TC]):
|
|||||||
d_key: str,
|
d_key: str,
|
||||||
keytype: Type[TK],
|
keytype: Type[TK],
|
||||||
valuetype: TC,
|
valuetype: TC,
|
||||||
store_default: bool = False) -> None:
|
store_default: bool = True) -> None:
|
||||||
super().__init__(d_key)
|
super().__init__(d_key)
|
||||||
self.d_value = valuetype
|
self.d_value = valuetype
|
||||||
|
|
||||||
|
|||||||
@ -130,7 +130,7 @@ class SimpleValue(TypedValue[T]):
|
|||||||
class StringValue(SimpleValue[str]):
|
class StringValue(SimpleValue[str]):
|
||||||
"""Value consisting of a single string."""
|
"""Value consisting of a single string."""
|
||||||
|
|
||||||
def __init__(self, default: str = "", store_default: bool = False) -> None:
|
def __init__(self, default: str = "", store_default: bool = True) -> None:
|
||||||
super().__init__(default, store_default, str)
|
super().__init__(default, store_default, str)
|
||||||
|
|
||||||
|
|
||||||
@ -139,7 +139,7 @@ class OptionalStringValue(SimpleValue[Optional[str]]):
|
|||||||
|
|
||||||
def __init__(self,
|
def __init__(self,
|
||||||
default: Optional[str] = None,
|
default: Optional[str] = None,
|
||||||
store_default: bool = False) -> None:
|
store_default: bool = True) -> None:
|
||||||
super().__init__(default, store_default, str, allow_none=True)
|
super().__init__(default, store_default, str, allow_none=True)
|
||||||
|
|
||||||
|
|
||||||
@ -148,7 +148,7 @@ class BoolValue(SimpleValue[bool]):
|
|||||||
|
|
||||||
def __init__(self,
|
def __init__(self,
|
||||||
default: bool = False,
|
default: bool = False,
|
||||||
store_default: bool = False) -> None:
|
store_default: bool = True) -> None:
|
||||||
super().__init__(default, store_default, bool, (int, float))
|
super().__init__(default, store_default, bool, (int, float))
|
||||||
|
|
||||||
|
|
||||||
@ -157,7 +157,7 @@ class OptionalBoolValue(SimpleValue[Optional[bool]]):
|
|||||||
|
|
||||||
def __init__(self,
|
def __init__(self,
|
||||||
default: Optional[bool] = None,
|
default: Optional[bool] = None,
|
||||||
store_default: bool = False) -> None:
|
store_default: bool = True) -> None:
|
||||||
super().__init__(default,
|
super().__init__(default,
|
||||||
store_default,
|
store_default,
|
||||||
bool, (int, float),
|
bool, (int, float),
|
||||||
@ -207,7 +207,7 @@ class DateTimeValue(SimpleValue[datetime.datetime]):
|
|||||||
The default value for this is always the current time in UTC.
|
The default value for this is always the current time in UTC.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, store_default: bool = False) -> None:
|
def __init__(self, store_default: bool = True) -> None:
|
||||||
# Pass dummy datetime value as default just to satisfy constructor;
|
# Pass dummy datetime value as default just to satisfy constructor;
|
||||||
# we override get_default_data though so this doesn't get used.
|
# we override get_default_data though so this doesn't get used.
|
||||||
dummy_default = datetime.datetime.now(datetime.timezone.utc)
|
dummy_default = datetime.datetime.now(datetime.timezone.utc)
|
||||||
@ -226,7 +226,7 @@ class DateTimeValue(SimpleValue[datetime.datetime]):
|
|||||||
class OptionalDateTimeValue(SimpleValue[Optional[datetime.datetime]]):
|
class OptionalDateTimeValue(SimpleValue[Optional[datetime.datetime]]):
|
||||||
"""Value consisting of a datetime.datetime object or None."""
|
"""Value consisting of a datetime.datetime object or None."""
|
||||||
|
|
||||||
def __init__(self, store_default: bool = False) -> None:
|
def __init__(self, store_default: bool = True) -> None:
|
||||||
super().__init__(None,
|
super().__init__(None,
|
||||||
store_default,
|
store_default,
|
||||||
datetime.datetime,
|
datetime.datetime,
|
||||||
@ -240,7 +240,7 @@ class OptionalDateTimeValue(SimpleValue[Optional[datetime.datetime]]):
|
|||||||
class IntValue(SimpleValue[int]):
|
class IntValue(SimpleValue[int]):
|
||||||
"""Value consisting of a single int."""
|
"""Value consisting of a single int."""
|
||||||
|
|
||||||
def __init__(self, default: int = 0, store_default: bool = False) -> None:
|
def __init__(self, default: int = 0, store_default: bool = True) -> None:
|
||||||
super().__init__(default, store_default, int, (bool, float))
|
super().__init__(default, store_default, int, (bool, float))
|
||||||
|
|
||||||
|
|
||||||
@ -249,7 +249,7 @@ class OptionalIntValue(SimpleValue[Optional[int]]):
|
|||||||
|
|
||||||
def __init__(self,
|
def __init__(self,
|
||||||
default: int = None,
|
default: int = None,
|
||||||
store_default: bool = False) -> None:
|
store_default: bool = True) -> None:
|
||||||
super().__init__(default,
|
super().__init__(default,
|
||||||
store_default,
|
store_default,
|
||||||
int, (bool, float),
|
int, (bool, float),
|
||||||
@ -261,7 +261,7 @@ class FloatValue(SimpleValue[float]):
|
|||||||
|
|
||||||
def __init__(self,
|
def __init__(self,
|
||||||
default: float = 0.0,
|
default: float = 0.0,
|
||||||
store_default: bool = False) -> None:
|
store_default: bool = True) -> None:
|
||||||
super().__init__(default, store_default, float, (bool, int))
|
super().__init__(default, store_default, float, (bool, int))
|
||||||
|
|
||||||
|
|
||||||
@ -270,7 +270,7 @@ class OptionalFloatValue(SimpleValue[Optional[float]]):
|
|||||||
|
|
||||||
def __init__(self,
|
def __init__(self,
|
||||||
default: float = None,
|
default: float = None,
|
||||||
store_default: bool = False) -> None:
|
store_default: bool = True) -> None:
|
||||||
super().__init__(default,
|
super().__init__(default,
|
||||||
store_default,
|
store_default,
|
||||||
float, (bool, int),
|
float, (bool, int),
|
||||||
@ -282,7 +282,7 @@ class Float3Value(SimpleValue[Tuple[float, float, float]]):
|
|||||||
|
|
||||||
def __init__(self,
|
def __init__(self,
|
||||||
default: Tuple[float, float, float] = (0.0, 0.0, 0.0),
|
default: Tuple[float, float, float] = (0.0, 0.0, 0.0),
|
||||||
store_default: bool = False) -> None:
|
store_default: bool = True) -> None:
|
||||||
super().__init__(default, store_default)
|
super().__init__(default, store_default)
|
||||||
|
|
||||||
def __repr__(self) -> str:
|
def __repr__(self) -> str:
|
||||||
@ -315,7 +315,7 @@ class BaseEnumValue(TypedValue[T]):
|
|||||||
def __init__(self,
|
def __init__(self,
|
||||||
enumtype: Type[T],
|
enumtype: Type[T],
|
||||||
default: Optional[T] = None,
|
default: Optional[T] = None,
|
||||||
store_default: bool = False,
|
store_default: bool = True,
|
||||||
allow_none: bool = False) -> None:
|
allow_none: bool = False) -> None:
|
||||||
super().__init__()
|
super().__init__()
|
||||||
assert issubclass(enumtype, Enum)
|
assert issubclass(enumtype, Enum)
|
||||||
@ -388,7 +388,7 @@ class EnumValue(BaseEnumValue[TE]):
|
|||||||
def __init__(self,
|
def __init__(self,
|
||||||
enumtype: Type[TE],
|
enumtype: Type[TE],
|
||||||
default: TE = None,
|
default: TE = None,
|
||||||
store_default: bool = False) -> None:
|
store_default: bool = True) -> None:
|
||||||
super().__init__(enumtype, default, store_default, allow_none=False)
|
super().__init__(enumtype, default, store_default, allow_none=False)
|
||||||
|
|
||||||
|
|
||||||
@ -401,7 +401,7 @@ class OptionalEnumValue(BaseEnumValue[Optional[TE]]):
|
|||||||
def __init__(self,
|
def __init__(self,
|
||||||
enumtype: Type[TE],
|
enumtype: Type[TE],
|
||||||
default: TE = None,
|
default: TE = None,
|
||||||
store_default: bool = False) -> None:
|
store_default: bool = True) -> None:
|
||||||
super().__init__(enumtype, default, store_default, allow_none=True)
|
super().__init__(enumtype, default, store_default, allow_none=True)
|
||||||
|
|
||||||
|
|
||||||
@ -412,7 +412,7 @@ class CompoundValue(DataHandler):
|
|||||||
any number of Field instances within themself.
|
any number of Field instances within themself.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, store_default: bool = False) -> None:
|
def __init__(self, store_default: bool = True) -> None:
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self._store_default = store_default
|
self._store_default = store_default
|
||||||
|
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
<!-- THIS FILE IS AUTO GENERATED; DO NOT EDIT BY HAND -->
|
<!-- THIS FILE IS AUTO GENERATED; DO NOT EDIT BY HAND -->
|
||||||
<!--DOCSHASH=b06760caff5d35273e974c2601857348-->
|
<!--DOCSHASH=22f3aab06ee39a80fe0b0db52e6bef03-->
|
||||||
<h4><em>last updated on 2019-12-13 for Ballistica version 1.5.0 build 20001</em></h4>
|
<h4><em>last updated on 2019-12-14 for Ballistica version 1.5.0 build 20001</em></h4>
|
||||||
<p>This page documents the Python classes and functions in the 'ba' module,
|
<p>This page documents the Python classes and functions in the 'ba' module,
|
||||||
which are the ones most relevant to modding in Ballistica. If you come across something you feel should be included here or could be better explained, please <a href="mailto:support@froemling.net">let me know</a>. Happy modding!</p>
|
which are the ones most relevant to modding in Ballistica. If you come across something you feel should be included here or could be better explained, please <a href="mailto:support@froemling.net">let me know</a>. Happy modding!</p>
|
||||||
<hr>
|
<hr>
|
||||||
|
|||||||
@ -18,7 +18,7 @@
|
|||||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
# SOFTWARE.
|
# SOFTWARE.
|
||||||
# -----------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
"""Testing tests."""
|
"""Testing entity functionality."""
|
||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
@ -34,7 +34,6 @@ if TYPE_CHECKING:
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
# A smattering of enum value types...
|
|
||||||
@unique
|
@unique
|
||||||
class EnumTest(Enum):
|
class EnumTest(Enum):
|
||||||
"""Testing..."""
|
"""Testing..."""
|
||||||
@ -42,9 +41,15 @@ class EnumTest(Enum):
|
|||||||
SECOND = 1
|
SECOND = 1
|
||||||
|
|
||||||
|
|
||||||
|
class SubCompoundTest(entity.CompoundValue):
|
||||||
|
"""Testing..."""
|
||||||
|
subval = entity.Field('b', entity.BoolValue())
|
||||||
|
|
||||||
|
|
||||||
class CompoundTest(entity.CompoundValue):
|
class CompoundTest(entity.CompoundValue):
|
||||||
"""Testing..."""
|
"""Testing..."""
|
||||||
isubval = entity.Field('i', entity.IntValue(default=34532))
|
isubval = entity.Field('i', entity.IntValue(default=34532))
|
||||||
|
compoundlist = entity.CompoundListField('l', SubCompoundTest())
|
||||||
|
|
||||||
|
|
||||||
class CompoundTest2(CompoundTest):
|
class CompoundTest2(CompoundTest):
|
||||||
@ -58,8 +63,8 @@ class EntityTest(entity.Entity):
|
|||||||
sval = entity.Field('s', entity.StringValue(default='svvv'))
|
sval = entity.Field('s', entity.StringValue(default='svvv'))
|
||||||
bval = entity.Field('b', entity.BoolValue(default=True))
|
bval = entity.Field('b', entity.BoolValue(default=True))
|
||||||
fval = entity.Field('f', entity.FloatValue(default=1.0))
|
fval = entity.Field('f', entity.FloatValue(default=1.0))
|
||||||
grp = entity.CompoundField('g', CompoundTest2())
|
grp = entity.CompoundField('g', CompoundTest())
|
||||||
grp2 = entity.CompoundField('g2', CompoundTest())
|
grp2 = entity.CompoundField('g2', CompoundTest2())
|
||||||
enumval = entity.Field('e', entity.EnumValue(EnumTest, default=None))
|
enumval = entity.Field('e', entity.EnumValue(EnumTest, default=None))
|
||||||
enumval2 = entity.Field(
|
enumval2 = entity.Field(
|
||||||
'e2', entity.OptionalEnumValue(EnumTest, default=EnumTest.SECOND))
|
'e2', entity.OptionalEnumValue(EnumTest, default=EnumTest.SECOND))
|
||||||
@ -71,17 +76,13 @@ class EntityTest(entity.Entity):
|
|||||||
fval2 = entity.Field('f2', entity.Float3Value())
|
fval2 = entity.Field('f2', entity.Float3Value())
|
||||||
|
|
||||||
|
|
||||||
class EntityTest2(entity.EntityMixin, CompoundTest2):
|
# noinspection PyTypeHints
|
||||||
"""test."""
|
|
||||||
|
|
||||||
|
|
||||||
def test_entity_values() -> None:
|
def test_entity_values() -> None:
|
||||||
"""Test various entity assigns for value and type correctness."""
|
"""Test various entity assigns for value and type correctness."""
|
||||||
ent = EntityTest()
|
ent = EntityTest()
|
||||||
|
|
||||||
# Simple int field.
|
# Simple int field.
|
||||||
with pytest.raises(TypeError):
|
with pytest.raises(TypeError):
|
||||||
# noinspection PyTypeHints
|
|
||||||
ent.ival = 'strval' # type: ignore
|
ent.ival = 'strval' # type: ignore
|
||||||
assert static_type_equals(ent.ival, int)
|
assert static_type_equals(ent.ival, int)
|
||||||
assert isinstance(ent.ival, int)
|
assert isinstance(ent.ival, int)
|
||||||
@ -89,23 +90,32 @@ def test_entity_values() -> None:
|
|||||||
ent.ival = 346
|
ent.ival = 346
|
||||||
assert ent.ival == 346
|
assert ent.ival == 346
|
||||||
|
|
||||||
|
# Simple float field.
|
||||||
|
with pytest.raises(TypeError):
|
||||||
|
ent.fval = "foo" # type: ignore
|
||||||
|
assert static_type_equals(ent.fval, float)
|
||||||
|
ent.fval = 2
|
||||||
|
ent.fval = True
|
||||||
|
ent.fval = 1.0
|
||||||
|
|
||||||
# Simple str/int dict field.
|
# Simple str/int dict field.
|
||||||
assert 'foo' not in ent.str_int_dict
|
assert 'foo' not in ent.str_int_dict
|
||||||
with pytest.raises(TypeError):
|
with pytest.raises(TypeError):
|
||||||
# noinspection PyTypeHints
|
|
||||||
ent.str_int_dict[0] = 123 # type: ignore
|
ent.str_int_dict[0] = 123 # type: ignore
|
||||||
with pytest.raises(TypeError):
|
with pytest.raises(TypeError):
|
||||||
# noinspection PyTypeHints
|
|
||||||
ent.str_int_dict['foo'] = 'bar' # type: ignore
|
ent.str_int_dict['foo'] = 'bar' # type: ignore
|
||||||
ent.str_int_dict['foo'] = 123
|
ent.str_int_dict['foo'] = 123
|
||||||
assert static_type_equals(ent.str_int_dict['foo'], int)
|
assert static_type_equals(ent.str_int_dict['foo'], int)
|
||||||
assert ent.str_int_dict['foo'] == 123
|
assert ent.str_int_dict['foo'] == 123
|
||||||
|
|
||||||
|
# Compound value inheritance.
|
||||||
|
assert ent.grp2.isubval2 == 3453
|
||||||
|
assert ent.grp2.isubval == 34532
|
||||||
|
|
||||||
# Compound list field.
|
# Compound list field.
|
||||||
with pytest.raises(IndexError):
|
with pytest.raises(IndexError):
|
||||||
print(ent.compoundlist[0])
|
print(ent.compoundlist[0])
|
||||||
with pytest.raises(TypeError):
|
with pytest.raises(TypeError):
|
||||||
# noinspection PyTypeHints
|
|
||||||
ent.compoundlist[0] = 123 # type: ignore
|
ent.compoundlist[0] = 123 # type: ignore
|
||||||
assert len(ent.compoundlist) == 0
|
assert len(ent.compoundlist) == 0
|
||||||
assert not ent.compoundlist
|
assert not ent.compoundlist
|
||||||
@ -113,3 +123,95 @@ def test_entity_values() -> None:
|
|||||||
assert ent.compoundlist
|
assert ent.compoundlist
|
||||||
assert len(ent.compoundlist) == 1
|
assert len(ent.compoundlist) == 1
|
||||||
assert static_type_equals(ent.compoundlist[0], CompoundTest)
|
assert static_type_equals(ent.compoundlist[0], CompoundTest)
|
||||||
|
|
||||||
|
# Enum value
|
||||||
|
with pytest.raises(ValueError):
|
||||||
|
ent.enumval = None # type: ignore
|
||||||
|
assert ent.enumval == EnumTest.FIRST
|
||||||
|
|
||||||
|
# Optional Enum value
|
||||||
|
ent.enumval2 = None
|
||||||
|
assert ent.enumval2 is None
|
||||||
|
|
||||||
|
# Nested compound values
|
||||||
|
assert not ent.grp.compoundlist
|
||||||
|
val = ent.grp.compoundlist.append()
|
||||||
|
assert static_type_equals(val, SubCompoundTest)
|
||||||
|
assert static_type_equals(ent.grp.compoundlist[0], SubCompoundTest)
|
||||||
|
assert static_type_equals(ent.grp.compoundlist[0].subval, bool)
|
||||||
|
|
||||||
|
|
||||||
|
class EntityTestMixin(entity.EntityMixin, CompoundTest2):
|
||||||
|
"""A test entity created from a compound using a mixin class."""
|
||||||
|
|
||||||
|
|
||||||
|
def test_entity_mixin() -> None:
|
||||||
|
"""Testing our mixin entity variety."""
|
||||||
|
ent = EntityTestMixin()
|
||||||
|
assert static_type_equals(ent.isubval2, int)
|
||||||
|
assert ent.isubval2 == 3453
|
||||||
|
|
||||||
|
|
||||||
|
def test_key_uniqueness() -> None:
|
||||||
|
"""Make sure entities reject multiple fields with the same key."""
|
||||||
|
|
||||||
|
# Make sure a single entity with dup keys fails:
|
||||||
|
with pytest.raises(RuntimeError):
|
||||||
|
|
||||||
|
class EntityKeyTest(entity.Entity):
|
||||||
|
"""Test entity with invalid duplicate keys."""
|
||||||
|
ival = entity.Field('i', entity.IntValue())
|
||||||
|
sval = entity.Field('i', entity.StringValue())
|
||||||
|
|
||||||
|
_ent = EntityKeyTest()
|
||||||
|
|
||||||
|
# Make sure we still get an error if the duplicate keys come from
|
||||||
|
# different places in the class hierarchy.
|
||||||
|
with pytest.raises(RuntimeError):
|
||||||
|
|
||||||
|
class EntityKeyTest2(entity.Entity):
|
||||||
|
"""Test entity with invalid duplicate keys."""
|
||||||
|
ival = entity.Field('i', entity.IntValue())
|
||||||
|
|
||||||
|
class EntityKeyTest3(EntityKeyTest2):
|
||||||
|
"""Test entity with invalid duplicate keys."""
|
||||||
|
sval = entity.Field('i', entity.StringValue())
|
||||||
|
|
||||||
|
_ent2 = EntityKeyTest3()
|
||||||
|
|
||||||
|
|
||||||
|
def test_data_storage_and_fetching() -> None:
|
||||||
|
"""Test store_default option for entities."""
|
||||||
|
|
||||||
|
class EntityTestD(entity.Entity):
|
||||||
|
"""Testing store_default off."""
|
||||||
|
ival = entity.Field('i', entity.IntValue(default=3,
|
||||||
|
store_default=False))
|
||||||
|
|
||||||
|
class EntityTestD2(entity.Entity):
|
||||||
|
"""Testing store_default on (the default)."""
|
||||||
|
ival = entity.Field('i', entity.IntValue(default=3))
|
||||||
|
|
||||||
|
# This guy should get pruned when its got a default value.
|
||||||
|
testd = EntityTestD()
|
||||||
|
assert testd.ival == 3
|
||||||
|
assert testd.pruned_data() == {}
|
||||||
|
testd.ival = 4
|
||||||
|
assert testd.pruned_data() == {'i': 4}
|
||||||
|
testd.ival = 3
|
||||||
|
assert testd.pruned_data() == {}
|
||||||
|
|
||||||
|
# Make sure our pretty/prune json options work.
|
||||||
|
assert testd.to_json_str() == '{}'
|
||||||
|
assert testd.to_json_str(prune=False) == '{"i":3}'
|
||||||
|
assert testd.to_json_str(prune=False, pretty=True) == ('{\n'
|
||||||
|
' "i": 3\n'
|
||||||
|
'}')
|
||||||
|
# This guy should never get pruned...
|
||||||
|
testd2 = EntityTestD2()
|
||||||
|
assert testd2.ival == 3
|
||||||
|
assert testd2.pruned_data() == {'i': 3}
|
||||||
|
testd2.ival = 4
|
||||||
|
assert testd2.pruned_data() == {'i': 4}
|
||||||
|
testd2.ival = 3
|
||||||
|
assert testd2.to_json_str(prune=True) == '{"i":3}'
|
||||||
|
|||||||
@ -65,9 +65,9 @@ CMD_LOGOUT = 'logout'
|
|||||||
CMD_PUTASSETPACK = 'putassetpack'
|
CMD_PUTASSETPACK = 'putassetpack'
|
||||||
CMD_HELP = 'help'
|
CMD_HELP = 'help'
|
||||||
|
|
||||||
|
# Note to self: keep this synced with server-side logic.
|
||||||
ASSET_PACKAGE_NAME_VALID_CHARS = 'abcdefghijklmnopqrstuvwxyz0123456789_'
|
ASSET_PACKAGE_NAME_VALID_CHARS = 'abcdefghijklmnopqrstuvwxyz0123456789_'
|
||||||
ASSET_PACKAGE_NAME_MAX_LENGTH = 32
|
ASSET_PACKAGE_NAME_MAX_LENGTH = 32
|
||||||
|
|
||||||
ASSET_PATH_VALID_CHARS = 'abcdefghijklmnopqrstuvwxyz0123456789_'
|
ASSET_PATH_VALID_CHARS = 'abcdefghijklmnopqrstuvwxyz0123456789_'
|
||||||
ASSET_PATH_MAX_LENGTH = 128
|
ASSET_PATH_MAX_LENGTH = 128
|
||||||
|
|
||||||
|
|||||||
@ -185,12 +185,16 @@ def static_type_equals(value: Any, statictype: Type) -> bool:
|
|||||||
wanttype = testfile.linetypes_wanted[linenumber]
|
wanttype = testfile.linetypes_wanted[linenumber]
|
||||||
mypytype = testfile.linetypes_mypy[linenumber]
|
mypytype = testfile.linetypes_mypy[linenumber]
|
||||||
|
|
||||||
# Do some filtering of Mypy types to simple python ones.
|
# Do some filtering of Mypy types so we can compare to simple python ones.
|
||||||
# (ie: 'builtins.list[builtins.int*]' -> int)
|
# (ie: 'builtins.list[builtins.int*]' -> int)
|
||||||
|
# Note to self: perhaps we'd want a fallback form where we can pass a
|
||||||
|
# type as a string if we want to match the exact mypy value?...
|
||||||
mypytype = mypytype.replace('*', '')
|
mypytype = mypytype.replace('*', '')
|
||||||
mypytype = mypytype.replace('?', '')
|
mypytype = mypytype.replace('?', '')
|
||||||
mypytype = mypytype.replace('builtins.int', 'int')
|
mypytype = mypytype.replace('builtins.int', 'int')
|
||||||
|
mypytype = mypytype.replace('builtins.float', 'float')
|
||||||
mypytype = mypytype.replace('builtins.list', 'List')
|
mypytype = mypytype.replace('builtins.list', 'List')
|
||||||
|
mypytype = mypytype.replace('builtins.bool', 'bool')
|
||||||
mypytype = mypytype.replace('typing.Sequence', 'Sequence')
|
mypytype = mypytype.replace('typing.Sequence', 'Sequence')
|
||||||
|
|
||||||
# temp3.FooClass -> FooClass
|
# temp3.FooClass -> FooClass
|
||||||
|
|||||||
@ -443,11 +443,14 @@ class App:
|
|||||||
# Check our packages and make sure all subdirs contain and __init__.py
|
# Check our packages and make sure all subdirs contain and __init__.py
|
||||||
# (I tend to forget this sometimes)
|
# (I tend to forget this sometimes)
|
||||||
packagedirs = ['tools/efrotools']
|
packagedirs = ['tools/efrotools']
|
||||||
script_asset_dir = 'assets/src/data/scripts'
|
|
||||||
for name in os.listdir(script_asset_dir):
|
# (Assume all dirs under these dirs are packages)
|
||||||
# (Assume all dirs under our script assets dir are packages)
|
dirs_of_packages = ['assets/src/data/scripts', 'tests']
|
||||||
if os.path.isdir(os.path.join(script_asset_dir)):
|
for dir_of_packages in dirs_of_packages:
|
||||||
packagedirs.append(os.path.join(script_asset_dir, name))
|
for name in os.listdir(dir_of_packages):
|
||||||
|
if (not name.startswith('.') and os.path.isdir(
|
||||||
|
os.path.join(dir_of_packages, name))):
|
||||||
|
packagedirs.append(os.path.join(dir_of_packages, name))
|
||||||
|
|
||||||
for packagedir in packagedirs:
|
for packagedir in packagedirs:
|
||||||
for root, _dirs, files in os.walk(packagedir):
|
for root, _dirs, files in os.walk(packagedir):
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user